Robert Basic's blog

Archives for February, 2017

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.