I am trying to format PHP files using the following function:

function! PHPCSFixer()                                                                                   
    let l:fixer_path = '$HOME/.devtools/php/php-cs-fixer-v2.phar'               
    let l:config_path = '$HOME/.devtools/php/php-cs-fixer.conf'                 
    let l:cmd = l:fixer_path . ' fix --quiet --config="' . l:config_path . '" -- "' . expand('%:p') . '"'
    :call system(l:cmd)                                                         
command! PHPCSFixer call PHPCSFixer()                                           
autocmd BufWritePost *.php silent :PHPCSFixer                                   

It just run the following script on the current file (https://github.com/FriendsOfPHP/PHP-CS-Fixer).

The problem is that every once in a while when I save (:w) a PHP file a warning pops up:

WARNING: The file has been changed since reading it!!!
Do you really want to write to it (y/n)?

I guess there is a timing problem somewhere but I don't know how/what to do to fix it...

Can someone point me to the right direction on this issue?

  • Welcome to Vi and Vim! You may want to investigate whether or not that script can run as a filter of stdin to stdout—it would avoid this problem altogethee
    – D. Ben Knoble
    Commented Jul 29, 2020 at 16:23
  • Hmm I don't understand what you mean by that, can you explain that a little more? Thanks!
    – Jesus_21
    Commented Jul 30, 2020 at 18:49
  • cat, sort, awk, sed, grep, these are all commands that take input, filter it (or not, in the case of cat), and produce output. This makes it very easy to filter a buffer through the command without needing to :edit! afterwards. Can your formatter take the code to format as standard input and produce the result on standard output, or does it only work by modifying files in-place?
    – D. Ben Knoble
    Commented Jul 30, 2020 at 18:52
  • OK, I get your point now. The fixer does take input from stdin but it does not output the result to stdout :| There is issues opened on the git repo of the fixer but they don't seem to plan that... The stdin input is pretty useless as far as I can tell. I guess I can write to a tmp file, fix it, cat to the buffer :/
    – Jesus_21
    Commented Jul 31, 2020 at 8:33

1 Answer 1


I came up with the following solution:

" make a tmp file & write buffer content to it
let l:tmp = system('mktemp')
execute 'write! ' . l:tmp

" fix the tmp file
let l:cmd = l:fixer_path . ' fix --quiet --config="' . l:config_path . '" -- "' . l:tmp . '"'
call system(l:cmd)

" cat tmp file to buffer & keep cursor pos
let l:save_cursor = getpos('.')
execute '%!cat ' . l:tmp
call setpos('.', l:save_cursor)

execute '!rm ' . l:tmp

Not very elegant I guess but working

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.