Emacs and PHP: On-the-fly syntax checking with Flymake

The dreaded white screen of nothingness usually means that I’ve misplaced a quotation mark or brace somewhere in my PHP code. On-the-fly syntax checking in Eclipse helped me find those errors quickly because I could scan the right scrollbar for areas marked with red. I knew that shifting to Emacs wouldn’t automatically cure me of the propensity to mismatch my parentheses. If I could get on-the-fly syntax checking working in Emacs, I’d save myself a lot of time.

It took me a while to sort out various configuration problems. Most were due to the fact that PHP didn’t report parse errors with my original configuration, even though I had uncommented the line after “Show only errors”. As it turned out, the error_reporting option in php.ini needs to include E_PARSE in order for php to report parsing error details. Here’s the value I’m currently using in /etc/php.ini:

error_reporting = E_ERROR|E_COMPILE_ERROR|E_CORE_ERROR|E_PARSE

Flymake is the package responsible for on-the-fly syntax checking in Emacs. Out of the box, it supports C, C++, XML, XHTML, Perl, Java, TeX, and IDL. To load Flymake and add support for PHP, add the following to your ~/.emacs:

(require 'flymake)

(defun flymake-php-init ()
  "Use php to check the syntax of the current file."
  (let* ((temp (flymake-init-create-temp-buffer-copy 'flymake-create-temp-inplace))
	 (local (file-relative-name temp (file-name-directory buffer-file-name))))
    (list "php" (list "-f" local "-l"))))

(add-to-list 'flymake-err-line-patterns 
  '("\\(Parse\\|Fatal\\) error: +\\(.*?\\) in \\(.*?\\) on line \\([0-9]+\\)$" 3 4 nil 2))

(add-to-list 'flymake-allowed-file-name-masks '("\\.php$" flymake-php-init))

;; Drupal-type extensions
(add-to-list 'flymake-allowed-file-name-masks '("\\.module$" flymake-php-init))
(add-to-list 'flymake-allowed-file-name-masks '("\\.install$" flymake-php-init))
(add-to-list 'flymake-allowed-file-name-masks '("\\.inc$" flymake-php-init))
(add-to-list 'flymake-allowed-file-name-masks '("\\.engine$" flymake-php-init))

(add-hook 'php-mode-hook (lambda () (flymake-mode 1)))
(define-key php-mode-map '[M-S-up] 'flymake-goto-prev-error)
(define-key php-mode-map '[M-S-down] 'flymake-goto-next-error)

Evaluate that code, open one of your PHP files, and intentionally break it. The syntax error should be highlighted. To change the highlighting, move your point to the error and type M-x customize-face. Accept the default (flymake-errline), then customize it as desired. Don’t forget to save your customizations for future sessions.

If your syntax errors are not highlighted and you get a message like this:

Configuration error occured while running (php -f test_flymake.php -l). flymake will be switched OFF

double-check your /etc/php.ini to make sure that E_PARSE is included as one of the options for error_reporting. You can check the output by running php -f yourfile.php -l. You should see the line number of the parse error. Make sure that this matches the regular expression added to flymake-err-line-patterns in your ~/.emacs. If your PHP returns a slightly different message, modify your flymake-err-line-patterns accordingly.

Flymake can save you a lot of programmer frustration for the cost of a little CPU time. Use it to check for errors before you save files or commit them to your source code control repository, and you and other developers will be much happier.

(UPDATE: Fixed typo in flymake-php-init)

5 Pingbacks/Trackbacks

  • Graham

    Thanks for the tip! However, I get the following error when using your suggested configuration:

    let*: Symbol’s value as variable is void: temp-file

    I tracked it down to a typo in the let* expression. I think the first part of the expression should be:

    (let* ((temp-file (flymake-init-create-temp-buffer-copy ‘flymake-create-temp-inplace))

    • http://sachachua.com Sacha Chua

      Oops, good catch. =) That’s what happens when I try to modify code in WordPress instead of Emacs… =)

  • http://atomized.org/ Ian Eure

    I’d recommend changing your error_reporting value to E_ALL at least, and shooting for E_STRICT is a good thing.

    I have a similar setup, but using compile-mode. It’s a little more concise:

    (load-library "compile.el")
    (pushnew '(php "syntax error.* in \\(.*\\) on line \\([0-9]+\\)$" 1 2)
             compilation-error-regexp-alist-alist)
    
    (setq compilation-error-regexp-alist
          (append (list 'php) compilation-error-regexp-alist))
    
    (defun php-lint ()
      "Performs a PHP lint-check on the current file."
      (interactive)
      (compile (concat "php -l -f \"" (buffer-file-name) "\"")))
    
    (define-key php-mode-map "\C-c\C-l" 'php-lint)
    (define-key osx-key-mode-map "\C-x~" 'previous-error)
    

    You get the same C-x ` binding to take you to the next error as the normal compile-mode, and C-x ~ takes you to the previous.

    • http://sachachua.com Sacha Chua

      I would change to E_ALL or E_STRICT, but we’re using a lot of third-party modules I’d probably need to modify, and I might not know each of them well enough to make changes without introducing new bugs. =) I’ll give it a try, though!

      Do you find that making php-lint a separate step in your process works out well for you? I might shift to that, because I really don’t need flymake running all the time when I know I’m writing partial code. (“I’m not done typing the function definition in yet! Stop complaining about the braces…”) =)

  • http://20y.hu/ slink

    There is syntax checking for PHP in flymake.el shipped with Emacs 23.

  • Pingback: Weekly review - week ending Aug 1-ish | sacha chua :: enterprise 2.0 consultant, storyteller, geek

  • Jake

    Emacs 23’s flymake now includes support for php. You should be able to get the proper effect with:

    (require ‘flymake)
    (add-hook ‘php-mode-hook (lambda() (flymake-mode 1)))
    (define-key php-mode-map ‘[M-S-up] ‘flymake-goto-prev-error)
    (define-key php-mode-map ‘[M-S-down] ‘flymake-goto-next-error)

  • Jason Murray

    Nice post, this was one of the missing features that prevented me from moving from Eclipse PDT to Emacs full time for development.

  • http://www.dave-cohen.com Dave Cohen

    Sacha,

    Thanks for sharing. While the comments are correct that flymake-php-init is already defined in my version of emacs. I still copied your Drupal-type file extensions and that’s what I needed to get it working for my drupal files.

    Now on my wish list is to run Drupal’s coder module in emacs’ compile mode. Is there an elisp expert out there who can tackle that?

    -Dave

  • slink

    @Dave: how do you invoke the Coder module? Do you run it via drush? Typing `M-x compile` allows you to specify the compile command to run that can be any drush command as well.

  • Pingback: ECAE — Shopex电子商务云的梦想空间 » [Denny] Emacs — fundanmental features

  • Pingback: ECAE — Shopex电子商务云的梦想空间 » [Denny] Emacs — fundanmental features

  • Pingback: [Denny] Emacs — fundanmental features | Denny: A indie developer

  • Pingback: Emacs — fundanmental features | Raw notes here. http://dennyzhang.com is better