Robert Basic's blog

Loading fixtures for a Symfony app in Behat tests

by Robert Basic on March 21, 2017.

Performing end to end testing of any application requires from us to have a set of reliable test data in the database.

If we write a Symfony application and use Behat to do the end to end testing, the we can use the Doctrine fixtures bundle to create the required fixture loaders and load them in our Behat scenarios when required, using the BeforeScenario hook.

Install Doctrine fixtures bundle

Using composer we can install the Doctrine fixtures bundle:

composer require --dev doctrine/doctrine-fixtures-bundle:2.3.0

and enable the bundle in the AppKernel:

app/AppKernel.php

diff --git a/app/AppKernel.php b/app/AppKernel.php
index 0d22098..c30e863 100644
--- a/app/AppKernel.php
+++ b/app/AppKernel.php
@@ -27,6 +27,7 @@ class AppKernel extends Kernel
             $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
             $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
             $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();
+            $bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle();
         }

         return $bundles;

Write a fixture loader

Now we can write a fixture loader. Writing fixture loaders is explained well in the official documentation.

Here’s an example fixture loader for the FOSUser bundle, creating users for the application:

src/AppBundle/DataFixtures/ORM/LoadUserData.php

<?php

namespace AppBundle\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class LoadUserData implements FixtureInterface, ContainerAwareInterface
{
    /**
     * @var ContainerInterface
     */
    private $container;

    public function setContainer(ContainerInterface $container = null)
    {
        $this->container = $container;
    }

    public function load(ObjectManager $manager)
    {
        foreach ($this->getData() as $data) {
            $userManager = $this->container->get('fos_user.user_manager');

            $user = $userManager->createUser();

            $user->setUsername($data['username']);
            $user->setUsernameCanonical($data['username']);
            $user->setPlainPassword($data['password']);

            $user->setEmail($data['email']);
            $user->setEmailCanonical($data['email']);

            $user->addRole($data['role']);
            $user->setEnabled($data['enabled']);

            $userManager->updateUser($user, true);
        }
    }

    private function getData()
    {
        return [
            [
                'username' => 'admin',
                'password' => 'adminpassword',
                'email' => 'admin@email.com',
                'firstname' => 'Boss',
                'lastname' => 'Big',
                'role' => 'ROLE_ADMIN',
                'enabled' => true,
            ],
        ];
    }
}

Do note that the LoadUserData fixture loader also implements the ContainerAwareInterface, meaning that it will get an instance of the ContainertInterface when invoked through the bin/console doctrine:fixtures:load console command.

We use this instance of the container to get the user manager from the FOSUser bundle. In turn, we use the user manager to create and update the users we are loading with this fixture loader.

Rest of it is straightforward — we set the username, email, password, role on the user and update it. The user manager will handle the password encryption as per the application configuration, saving the user to the database, and so on…

Load fixtures in Behat tests

The Behat test file has a bit more to it.

It requires the Behat Symfony2 extension (so far it works for Symfony 3 applications as well!)

composer require --dev behat/symfony2-extension:2.1.1

Next, we need to tell Behat to pass an instance of the AppKernel to our test. We do so through the behat.yml file:

behat.yml

default:
    suites:
        my_feature:
            contexts:
                - 'FeatureContext'
                    kernel: '@kernel'
    extensions:
        Behat\Symfony2Extension:
            kernel:
                class: AppKernel

This will allow our FeatureContext test file to get an instance of a KernelInterface, the AppKernel, through the constructor:

features/bootstrap/FeatureContext.php

<?php

use Doctrine\ORM\Tools\SchemaTool;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;

use AppBundle\DataFixtures\ORM\LoadUserData;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Loader;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;


class FeatureContext
{
    protected $container;

    protected $entityManager;

    public function __construct(KernelInterface $kernel)
    {
        $this->container = $kernel->getContainer();

        $this->entityManager = $this->container->get('doctrine.orm.default_entity_manager');

        $schemaTool = new SchemaTool($this->entityManager);
        $schemaTool->dropDatabase();
        $metadata = $this->entityManager->getMetadataFactory()->getAllMetadata();
        $schemaTool->createSchema($metadata);
    }
}

We get the container from the kernel and the entity manager from the container. No need to setup anything, it’s all taken care of under the hood.

We also use this “occasion” to make sure that the database schema is recreated before the test is ran.

The final step is to load the fixtures data we need in our FeatureContext.php test file:

    /**
     * @BeforeScenario
     */
    public function loadDataFixtures(BeforeScenarioScope $scope)
    {
        $userData = new LoadUserData();
        $userData->setContainer($this->container);

        $loader = new Loader();
        $loader->addFixture($userData);

        $purger = new ORMPurger();
        $purger->setPurgeMode(ORMPurger::PURGE_MODE_DELETE);

        $executor = new ORMExecutor($this->entityManager, $purger);
        $executor->execute($loader->getFixtures());
    }

The main thing to note here is that the container is not automatically set on the LoadUserData fixture loader, so we need to do that manually by calling the setContainer method with $this->container as an argument. Remember, we got the container instance from the kernel in the class constructor.

We tell the loader which fixture to load, the purger to delete any existing records from the database and finally the executor to load our fixtures.

Now when we run the Behat test suite, the database, in the environment against which we run the tests, will have a fresh set of data every time.

Happy hackin’!

Tags: behat, fixtures, php, symfony.
Categories: Development, Programming.

Current Vim setup for PHP development

by Robert Basic on February 10, 2017.

I made some changes to my Vim setup for PHP development recently, so it’s time to write it all down. I’m more than sure that I’ll break it soon and won’t be able to remember all the things I did to have the current setup.

Some new plugins popped up on my radar, I tweaked some older plugins and I even wrote one for PHPStan!

Last year I wrote how I got really good tag support in Vim, so I’ll first expand on that.

Gutentags

Gutentags is probably the plugin that had the biggest impact on my workflow. It made possible many other functionalities and plugins to just work.

First thing I have configured for it is the location for the tag files:

" Where to store tag files
let g:gutentags_cache_dir = '~/.vim/gutentags'

The second is a more “aggressive” excluding of files from generating tags:

let g:gutentags_exclude = ['*.css', '*.html', '*.js', '*.json', '*.xml',
                            \ '*.phar', '*.ini', '*.rst', '*.md',
                            \ '*vendor/*/test*', '*vendor/*/Test*',
                            \ '*vendor/*/fixture*', '*vendor/*/Fixture*',
                            \ '*var/cache*', '*var/log*']

Takes out a lot of rubbish.

I also have the following in the global ~/.ctags configuration file (gutentags uses ctags under the hood to generate the tags):

--PHP-kinds=+cfit-va

This way I get tags for classes, interfaces, functions, namespaces and traits, while variables and aliases are ignored to remove the noise level.

Jump to definition

I paired CtrlP’s CtrlPTag method with gutentags tag files to get jump to definition functionality:

map <silent> <leader>jd :CtrlPTag<cr><C-\>w

My complete CtrlP settings are here.

Current PHP class and method

A combination of three Vim plugins and a PHP phar file gives me the possibility to show in the status bar the current PHP class and method.

Lightline provides the status bar, tagbar to get the current tag, tagbar-phpctags to generate the tags for the current file using phpctags.

Use the following setting to tell tagbar about the phpctags binary:

let g:tagbar_phpctags_bin='~/.vim/phpctags'

This is the tagbar line for lightline:

'tagbar': '%{tagbar#currenttag("[%s]", "", "f")}',

See here how it all fits together.

Now that I wrote it all down, I should take a look if I could use the gutentags tag files for this. Seems like an awful lot of moving parts for a relatively small feature.

PHP namespaces

The vim-php-namespace plugin provides support for inserting use statements in PHP code. It will use the tag files generated by gutentags, there’s no need to set up anything for that.

I have the following mapping to insert the use statements:

function! IPhpInsertUse()
    call PhpInsertUse()
    call feedkeys('a',  'n')
endfunction
autocmd FileType php inoremap <Leader>pnu <Esc>:call IPhpInsertUse()<CR>
autocmd FileType php noremap <Leader>pnu :call PhpInsertUse()<CR>

and this one for expanding classes to get their fully qualified names:

function! IPhpExpandClass()
    call PhpExpandClass()
    call feedkeys('a', 'n')
endfunction
autocmd FileType php inoremap <Leader>pne <Esc>:call IPhpExpandClass()<CR>
autocmd FileType php noremap <Leader>pne :call PhpExpandClass()<CR>

I also let it to automatically sort the namespaces after inserting:

let g:php_namespace_sort_after_insert=1

Linting

ALE is an Asynchronous Lint Engine and it provides linting for Vim 8. It can do linting for a bunch of languages.

My configuration for it is:

let g:ale_linters = {
\   'php': ['php'],
\}
let g:ale_lint_on_save = 1
let g:ale_lint_on_text_changed = 0

Lints PHP files on save, in the background. A must have!

A promising completion engine for PHP

php.cd finally provides useful completion for PHP. It’s still rough around the edges, misses a feature or two, but I find it a lot better than any other completion engine I used before. No need to configure anything for it, just follow their installation instructions and that’s it. ^X^O all the things!

PHPStan in Vim

PHPStan is a static analysis tool for PHP and I wrote a small plugin for it, vim-phpstan. It calls phpstan from Vim and populates Vim’s quickfix list with the errors.

For now, the only possible configuration is to set the analyse level:

let g:phpstan_analyse_level = 2

Debugging

Sadly, there is no good PHP debugging client for Vim. Or none that I know of. There are a couple of them out there, but they seem long abandoned. I work on a standalone PHP debugging client, pugdebug, but it has it’s own set of problems as well (packaging on Linux is a nightmare).

Supporting plugins

Other “supporting” plugins are 2072/PHP-Indenting-for-VIm, 2072/vim-syntax-for-PHP, sirver/ultisnips, plugins from the sniphpets organization, ddrscott/vim-side-search, robertbasic/vim-argument-swapper.

I’m pretty happy with the current setup. Do you know maybe of any interesting plugin I’m missing? Let me know!

Happy hackin’!

Tags: ale, completion, gutentags, linting, namespaces, php, phpcd, phpctags, phpstan, plugins, tagbar, vim.
Categories: Development, Programming.

PHP-FPM security limit extensions issue

by Robert Basic on February 03, 2017.

For the first time ever I saw this error:

2017/02/03 11:45:04 [error] 14656#0: *1 FastCGI sent in stderr: "Access to the script '/var/www/web' has been
denied (see security.limit_extensions)" while reading response header from upstream, client: 127.0.0.1, server:
proj.loc, request: "GET / HTTP/1.1", upstream: "fastcgi://unix:/var/run/php-fpm/www.sock:", host: "proj.loc"

I mean… what? security.limit_extensions? I honestly never heard of this before.

The PHP manual describes it as:

Limits the extensions of the main script FPM will allow to parse. This can prevent configuration mistakes on the web server side. You should only limit FPM to .php extensions to prevent malicious users to use other extensions to execute php code. Default value: .php .phar

Basically to avoid executing what an application might consider as a non-PHP file as a PHP file.

OK, cool, but why am I getting this error?

The currently top answer on Google suggests setting the list of limited extensions to an empty string, to practically disable the security.limit_extensions configuration. That fixes the error, but I’m really not comfortable with setting a security related configuration to a blank value, especially when people smarter than me set that configuration to a sane default value.

There must be a better, proper way to fix this, and this does feel like I misconfigured something in the nginx/php-fpm stack.

Accessing a folder as a script?

The Access to the script '/var/www/web' has been denied part of the error messages also looks weird. Why would php-fpm try to access /var/www/web, which is a directory, as a script? Seems like it doesn’t see the actual PHP script, and that sounds awfully similar to that old, dreaded No input file specified error message.

And that one is, in most cases, caused by not including the fastcgi.conf params file in the location block in the nginx configuration files. I double checked the configuration file and yup, I missed to include the fastcgi params file:

server {
    # configuration for the server
    location ~ \.php$ {
        # configuration for php
        include fastcgi.conf; # << I missed this!
    }
}

I restarted nginx and everything works just fine, without touching the security.limit_extensions configuration.

Happy hackin’!

Tags: configuration, fastcgi, limit_extensions, nginx, php-fpm, security.
Categories: Development, Software.

Search and replace in visual selection in Vim

by Robert Basic on January 23, 2017.

The search and replace feature is very powerful in Vim. Just do a :help :s to see all the things it can do.

One thing that always bothered me though, is that when I select something with visual, try to do a search and replace on it, Vim actually does it on the entire line, not just on the selection.

What the…? There must be a way to this, right?

Right. It’s the \%V atom.

Instead of doing :'<,'>s/foo/bar/g to replace foo with bar inside the selection, which will replace all foo occurences with bar on the entire line, the correct way is to use the \%V atom and do :'<,'>s/\%Vfoo/bar/g.

I’m using this approach in the HugoHelperLink fuction of my Vim Hugo Helper plugin.

Happy hackin’!

Tags: replace, search, vim.
Categories: Blablabla, Software.

XFCE4 desktop zooming with the keyboard

by Robert Basic on January 19, 2017.

XFCE4 has a zoom feature available when the desktop composition is turned on. By default, holding the Alt key and scrolling up or down the mouse wheel, I can zoom in or out the entire desktop. Once zoomed in, it follows the mouse pointer as to which part of the desktop to show.

I prefer doing as much as possible from my keyboard, and to use the mouse only when necessary.

I don’t care much for desktop composition, the transparent windows and animations are not my thing.

Toggle desktop composition

Given that desktop composition is required for the zooming feature, I want it enabled only when I want to use the zoom feature itself.

Using the following command, I can toggle the composition on and off:

xfconf-query --channel=xfwm4 --property=/general/use_compositing --type=bool --toggle

xdotool to fake the mouse

xdotool is a nice little program that fakes keyboard and mouse input, among other things.

Using that, running the following command from the terminal, zooms in:

xdotool keydown Alt click 4 keyup Alt

and this command zooms out:

xdotool keydown Alt click 5 keyup Alt

Just to make all this even easier, I put these commands in a couple of bash scripts and added them as keyboard shortcuts.

Now I have Super C to toggle the desktop composition, Alt + to zoom in and Alt - to zoom out.

Happy hackin’!

Tags: accessibility, compositing, desktop, keyboard, mouse, xfce4, zoom.
Categories: Blablabla, Software.