Last updated September 24, 2013. Created by eddie@oho.com on April 22, 2006.
Edited by myglobaldata_gil, axel, semperos, a.ross. Log in to edit this page.

There are plenty of extensions for Emacs that you can use as a comfortable programming editor for PHP/Drupal. This documentation covers only basic set of features (like syntax highlighting and code templates). You'll find more tools related to programming in emacswiki.org.

You may try preconfigured set of tools bundled as «Emacs IDE for Drupal» — the project in early development though and may be unstable.

To enable syntax highlighting for PHP code, install PHP mode for Emacs. On Debian-based systems, try the following command:
sudo apt-get install php-elisp

Alternatively, you can save the php-mode.el file from here, save it under your ~/.emacs.d folder (or some sub-folder of that, if you prefer), then add the following to your ~/.emacs file:

; make sure the target directory is on your load-path
(add-to-list 'load-path "~/.emacs.d")
(require 'php-mode)

[UPDATED METHOD: The following major-mode for Drupal is a more robust solution than the original snippet. If you want to see the original code from this post, scroll further down.]

Then save the following code to a file called drupal-mode.el, put it in the same folder, and add (require 'drupal-mode) after the require statement for php-mode demonstrated above.

(Taken from Xen's comment below)

;;; drupal-mode.el --- major mode for Drupal coding
;;;###autoload
(define-derived-mode drupal-mode php-mode "Drupal"
  "Major mode for Drupal coding.\n\n\\{drupal-mode-map}"
  (setq c-basic-offset 2)
  (setq indent-tabs-mode nil)
  (setq fill-column 78)
  (setq show-trailing-whitespace t)
  (add-hook 'before-save-hook 'delete-trailing-whitespace)
  (c-set-offset 'case-label '+)
  (c-set-offset 'arglist-close 0)
  (c-set-offset 'arglist-intro '+) ; for FAPI arrays and DBTNG
  (c-set-offset 'arglist-cont-nonempty 'c-lineup-math) ; for DBTNG fields and values
  (run-mode-hooks 'drupal-mode-hook)
)
(provide 'drupal-mode)

You can then setup file associations in your ~/.emacs file. Here's an example that opens Drupal-specific file extensions with drupal-mode by default, but leaves *.php and *.inc files as php-mode by default. You can always explicitly switch major modes with M-x <name of mode>

  (add-to-list 'auto-mode-alist '("\\.\\(module\\|test\\|install\\|theme\\)$" . drupal-mode))
  (add-to-list 'auto-mode-alist '("\\.\\(php\\|inc\\)$" . php-mode))
  (add-to-list 'auto-mode-alist '("\\.info" . conf-windows-mode))

[ORIGINAL POST FOLLOWS]

This short snippet will extend the php-mode in Emacs to follow Drupal coding style. Add this to your $HOME/.emacs:

(defun drupal-mode ()
  "Drupal php-mode."
  (interactive)
  (php-mode)
  (message "Drupal mode activated.")
  (set 'tab-width 2)
  (set 'c-basic-offset 2)
  (set 'indent-tabs-mode nil)
  (c-set-offset 'case-label '+)
  (c-set-offset 'arglist-intro '+) ; for FAPI arrays and DBTNG
  (c-set-offset 'arglist-cont-nonempty 'c-lineup-math) ; for DBTNG fields and values
  ; More Drupal-specific customizations here
)
(defun setup-php-drupal ()
  ; Drupal
  (add-to-list 'auto-mode-alist '("\\.\\(module\\|test\\|install\\|theme\\)$" . drupal-mode))
  (add-to-list 'auto-mode-alist '("/drupal.*\\.\\(php\\|inc\\)$" . drupal-mode))
  (add-to-list 'auto-mode-alist '("\\.info" . conf-windows-mode))
)
(setup-php-drupal)

This was just for simplicity, in my system however, I replace the last line: ((setup-php)) with:

(provide 'setup-php-drupal)
(provide 'drupal-mode)

And instead of putting that in my ~/.emacs directly I put in a separate file e.g. ~/.emacs.d/drupal-mode.el. Then in my .emacs I have:

(add-to-list 'load-path "~/.emacs.d")
; My PHP setup
(require 'setup-php-drupal)
(setup-php-drupal)

This will automatically set you in drupal-mode if you load a .php, .module or .inc file from beneath a drupal* directory. You may also manually select drupal mode by hitting M-x drupal-mode.

Looking for support? Visit the Drupal.org forums, or join #drupal-support in IRC.

Comments

Thanks for posting this!

AFAIK the correct Drupal indentation for arrays looks like this:

<?php
$a
= array(
 
'foo' => 'bar',
 
'gaz' => 'gazonk',
) ;
?>

but for me, this mode results in

<?php
$a
= array(
          
'foo' => 'bar',
          
'gaz' => 'gazonk',
          ) ;
?>

Any tips for resolving this?

I didn't want to edit the page unless someone confirms the following works for them too.

In your .emacs:

(defun drupal-mode ()
  "Drupal php-mode."
  (interactive)
  (php-mode)
  (message "Drupal mode activated.")
  (set 'tab-width 2)
  (set 'c-basic-offset 2)
  (set 'indent-tabs-mode nil)
  (c-set-offset 'case-label '+)
  (c-set-offset 'arglist-intro '+) ; for FAPI arrays and DBTNG
  (c-set-offset 'arglist-cont-nonempty 'c-lineup-math) ; for DBTNG fields and values
  ; More Drupal-specific customizations here
)
(defconst my-php-style
  '((c-offsets-alist . (
    (arglist-close . c-lineup-close-paren) ; correct arglist closing parenthesis
   )))
  "My PHP Programming style"
)
(c-add-style "my-php-style" my-php-style)
(defun my-php-mode ()
  "My personal php-mode customizations."
  (c-set-style "my-php-style")
  ; More generic PHP customizations here
)
(defun setup-php ()
  ; PHP
  (add-hook 'php-mode-hook 'my-php-mode)
  ; Drupal
  (add-to-list 'auto-mode-alist '("\\.\\(module\\|test\\|install\\|theme\\)$" . drupal-mode))
  (add-to-list 'auto-mode-alist '("/drupal.*\\.\\(php\\|inc\\)$" . drupal-mode))
  (add-to-list 'auto-mode-alist '("\\.info" . conf-windows-mode))
  ; More startup-setup for PHP customizations to work here
)
(setup-php)

This was just for simplicity, in my system however, I replace the last line: ((setup-php)) with:

(provide 'setup-php)
(provide 'my-php-mode)

And instead of putting that in my ~/.emacs directly I put in a separate file e.g. ~/.emacs.d/setup-php.el. Then in my .emacs I have:
(add-to-list 'load-path "~/.emacs.d")
; My PHP setup
(require 'setup-php)
(setup-php)

--
Amr Mostafa (aka “alienbrain”)

Cheers AMR, that seems to have cleared up the array indentation issue nicely for me. Haven't given it extensive testing yet but will do so today :)

Thanks again!

This fixes the major indentation issues with arrays, except it also seems to reset the indentation width from 2 back to 4 (at least for me.)

My array formatting is fixed (after years of formatting arrays manually!), but my indentation is all 4 spaces, even though I've explicitly set it to 2 spaces.

Edit: I resolved this by commenting the following line:

;  (set 'tab-width 2)

It seems that specifying both tab-width and c-basic-offset as 2 makes it 4?

I've turned this into a proper major mode that extends php-mode, with all the advantages that gives:

;;; drupal-mode.el --- major mode for Drupal coding
;;;###autoload
(define-derived-mode drupal-mode php-mode "Drupal"
  "Major mode for Drupal coding.\n\n\\{drupal-mode-map}"
  (setq c-basic-offset 2)
  (setq indent-tabs-mode nil)
  (setq fill-column 78)
  (setq show-trailing-whitespace t)
  (add-hook 'before-save-hook 'delete-trailing-whitespace)
  (c-set-offset 'case-label '+)
  (c-set-offset 'arglist-close 0)
  (c-set-offset 'arglist-intro '+) ; for FAPI arrays and DBTNG
  (c-set-offset 'arglist-cont-nonempty 'c-lineup-math) ; for DBTNG fields and values
  (run-hooks 'drupal-mode-hook)
)
(provide 'drupal-mode)

This can be put into a file and loaded with (load [filename]).

Then you can modify the keyboard binding with:

(define-key drupal-mode-map (kbd "C-c d h") 'drupal-hook-insert)
(define-key drupal-mode-map (kbd "C-c d f") 'drupal-form-insert)

without mucking with the php-mode or global bindings. One should also be able to add more customizations to the drupal-mode-hook, but I haven't tested if that works.

In Emacs PHP-mode, the following code evaluates so that Emacs thinks the comment has closed at the end of first array entry:

<?php
/*
      $form['back'] = array(
        '#type' => 'submit',
        '#value' => 'Cancel',
        '#id' => 'new-contact-cancel'
      ) ;
*/

?>

This is because Emacs detects the '#' in the second line as another comment opening, and believes that this has an implicit closing comment at the end of the line. From the line starting '#value', the code is treated as though it's outside the comment.

Do others see the same issue with Emacs and PHP-mode?

This isn't specifically related to the config posted above, rather it is an issue with Emacs PHP mode which shows up with commented forms code using multi-line comments.

One workaround is to comment out code blocks using M-x comment-region (and M-x uncomment-region) rather than multi-line comment blocks, but it would be cool to figure a fix for PHP mode's comment detection regex.

I've had the same issue as well. I found this to be fixed in the latest version of php-mode though!

Now for the seasoned emacs-er this should have been enough instructions, for beginners like myself:

  • Go to http://php-mode.sourceforge.net/ and download the latest release.
  • Grab the php-mode.el from the package and add it to your ~/.emacs.d directory or wherever you stash your elisp ;)
  • Add (add-to-list 'load-path "~/.emacs.d") at the top of your ~/.emacs file.

Now that should do it! But If you want to make sure it was loaded and you are indeed running the latest and greatest:

  • Restart emacs (you can also use M-x eval-buffer).
  • Press M-: (that's Alt-Shift-;) and evaluate the following lisp which will confirm which version of php-mode we are running: (condition-case nil php-mode-version-number ((error nil) php-version)). If you see 1.5.0 (which is the latest as of 4th of April, 2009) then you are all set! Anything lower means you are still running some old version. Perhaps the right thing to do in this case is to find the other php-mode.el, wherever it is, and replace it with the new one.

Enjoy!

--
Amr Mostafa (aka “alienbrain”)

Need help setting this up on a Windows Vista. I just download emacs-23.1, but don't have a clue how to set this config up. Can anyone help me out in what I need to do?

_____________________________________________
Coding is like a box of chocolates!...

openSUSE 12.1
NVIDIA GeForce 9500 GT
Intel(R) Core(TM)2 Quad CPU Q9550 @ 2.83GHz

Cancel That! I found an excellent IDE in Netbeans. My style of an environment...

_____________________________________________
Coding is like a box of chocolates!...

openSUSE 12.1
NVIDIA GeForce 9500 GT
Intel(R) Core(TM)2 Quad CPU Q9550 @ 2.83GHz

Cancel That! I found an excellent IDE in Netbeans. My style of an environment...

_____________________________________________
Coding is like a box of chocolates!...

openSUSE 12.1
NVIDIA GeForce 9500 GT
Intel(R) Core(TM)2 Quad CPU Q9550 @ 2.83GHz

I use these for quickly generating some commonly-used snippets:

(defun drupal-module-name ()
  "Return name of module"
  (car (split-string (buffer-name) "\\.")))
(defun drupal-hook-insert (name)
  "Insert template for a drupal hook"
  (interactive "MEnter hook name:")
  (insert (concat "/**\n* Implementation of hook_" name "().\n*/\nfunction " (drupal-module-name) "_" name "() {\n\n}\n")))
(defun drupal-form-insert (name)
  "Insert templat for a drupal form"
  (interactive "MEnter form name:")
  (insert (concat "/**\n * Form builder for .\n */\nfunction " (drupal-module-name) "_" name "($form_state) {\n\n}\n"))
  (insert (concat "\n\n/**\n * Form validation handler for " (drupal-module-name) "_" name "().\n */\nfunction " (drupal-module-name) "_" name "_validate($form, &$form_state) {\n\n}"))
  (insert (concat "\n\n/**\n * Form submission handler for " (drupal-module-name) "_" name "().\n */\nfunction " (drupal-module-name) "_" name "_submit($form, &$form_state) {\n\n}")))
(global-set-key (kbd "C-c d h") 'drupal-hook-insert)
(global-set-key (kbd "C-c d f") 'drupal-form-insert)

Nothing really complicated here, just some basic filler, but it works and saves time!

Add to function body of drupal-mode to get fill-paragraph to work correctly in a doxygen comment block with lists of parameters.

(setq paragraph-start (concat paragraph-start "\\| \\* @[a-z]+")
paragraph-separate "$")

Does no one here know about .dir-settings.el (Emacs <= 23) and .dir-locals.el (Emacs 24) ???

This is the example from the Emacs Wiki:

((nil . ((indent-tabs-mode . t)
(tab-width . 4)
(fill-column . 80)))
(c-mode . ((c-file-style . "BSD")))
(java-mode . ((c-file-style . "BSD")))
("src/imported"
. ((nil . ((change-log-default-name . "ChangeLog.local"))))))

I love these. They go, once you delete the projects folder. They are not loaded, unless you open a file in the same folder or below.

Make love not war! Pobbe net klobbe!

The story:
http://www.cestfait.ch/content/yasnippet-drupal

If you have yasnippet installed, juste add this mode for php:
http://www.cestfait.ch/sites/default/files/php-mode.zip

And if you don't have yasnippet :
http://code.google.com/p/yasnippet/

etags supports PHP, but doesn't know about Drupal's extensions.

Run etags -R --langmap=php:+.module.install.inc in your Drupal root directory to generate a TAGS file which includes all .module, .install, and .inc files.

Then use M-x find-tag (normally bound to M-.) to use it.

Use a prefix arg to find the next match, if there is more than one (C-u M-., or something slightly easier to type, such as M-1 M-.).

Old versions of etags may not accept those arguments, but you can achieve the same results with something like:

find . -type f | etags -
find . -type f \( -name "*.module" -o -name "*.install" -o -name "*.inc" \) | etags -a --language=php -

There's also a recent Stack Overflow question asking how to improve tagging for PHP, but no answers as yet:
http://stackoverflow.com/questions/2916423/is-there-an-intelligent-php-e...

Here's something kind of fun, based on http://www.emacswiki.org/emacs/PhpMode#toc13
Press f1 to lookup a php function, M-f1 to open browser to php.net.

I've added some code to make M-f2 bring up api.drupal.org. I'd like to define my-drupal-function-lookup similar to my-php-function-lookup, but that's way beyond my elisp chops.

;; http://www.emacswiki.org/emacs/PhpMode#toc13
(add-hook 'php-mode-hook 'my-php-mode-stuff)
(defun my-php-mode-stuff ()
  (local-set-key (kbd "<f1>") 'my-php-function-lookup)
  (local-set-key (kbd "M-<f1>") 'my-php-symbol-lookup)
  (local-set-key (kbd "<f2>") 'my-drupal-symbol-lookup)
  (local-set-key (kbd "M-<f2>") 'my-drupal-symbol-lookup)
  )
(defun my-php-symbol-lookup ()
  (interactive)
  (let ((symbol (symbol-at-point)))
    (if (not symbol)
        (message "No symbol at point.")
      (browse-url (concat "http://php.net/manual-lookup.php?pattern="
                          (symbol-name symbol))))))
(defun my-drupal-symbol-lookup ()
  (interactive)
  (let ((symbol (symbol-at-point)))
    (if (not symbol)
        (message "No symbol at point.")
      (browse-url (concat "http://api.drupal.org/api/function/"
                          (symbol-name symbol)
                          "/6")))))
(defun my-php-function-lookup ()
  (interactive)
  (let* ((function (symbol-name (or (symbol-at-point)
                                    (error "No function at point."))))
         (buf (url-retrieve-synchronously (concat "http://php.net/manual-lookup.php?pattern=" function))))
    (with-current-buffer buf
      (goto-char (point-min))
      (let (desc)
        (when (re-search-forward "<div class="methodsynopsis dc-description">\\(\\(.\\|\n\\)*?\\)</div>" nil t)
          (setq desc
                (replace-regexp-in-string
                 " +" " "
                 (replace-regexp-in-string
                  "\n" ""
                  (replace-regexp-in-string "<.*?>" "" (match-string-no-properties 1)))))
          (when (re-search-forward "<p class="para rdfs-comment">\\(\\(.\\|\n\\)*?\\)</p>" nil t)
            (setq desc
                  (concat desc "\n\n"
                          (replace-regexp-in-string
                           " +" " "
                           (replace-regexp-in-string
                            "\n" ""
                            (replace-regexp-in-string "<.*?>" "" (match-string-no-properties 1))))))))
        (if desc
            (message desc)
          (message "Could not extract function info. Press M-F1 to go the description."))))
    (kill-buffer buf)))

For those new to Emacs, I strongly recommend emacs-starter-kit (personal favorite) or emacs-prelude. They add a bunch of sensible defaults (supported languages, theme, ido, shortcuts, etc) to make Emacs feel muuuuch nicer than you get out of the box. These packages are extremely popular, and most new to Emacs start here.

After you have one of those installed, then add the PHP & Drupal libraries/configurations presented in this post. Alternatively, jrbeeman has a starter package meant for Drupal, looks promising.

great tips all!

There are a few people who maintain an updated version of php-mode on github:
https://github.com/ejmr/php-mode

As far as I know this library plays better with current versions of PHP and contains other bug-fixes, etc.

Has anyone found anything better than geben for debugging Drupal in emacs?

I don't mean to dis geben, as I'm glad it exists. Still it always seems to open an unnecessary frame or steal focus. And if you leave it for a moment there's no easy way to find that buffer again.

I don't have the elisp chops to fix those things, so I'm wondering if anyone has an alternate xdebug setup via emacs.

I can't help with an alternative Xdebug interface (I suppose it's feasible that emacs-eclim might work with the Xdebug plugin for Eclipse, but that sounds like quite a lot of hoops); however there's no reason that you should have difficulty finding a buffer in Emacs, so I would suggest reading this: http://stackoverflow.com/questions/3145332 (read all of the answers, not just the accepted answer; there's some really good info in there).

I also highly recommend using winner-mode, which lets you get back to any previous window configuration (buffers, window splits & sizes, etc) in a given frame in a jiffy. I'd hate to be without it.

There's no real alternative to Geben, it's still the best xdebug for Emacs (albiet quite aged, unfortunately).

It should only be opening one additional window (no additional frames). I've had a problem when using geben with ECB, where ECB doubles any pop-up-window requests, so I get 4 windows. Disabling ECB was the solution, or turning off pop-up-window. Check if any other plugins are the culprit. As far as locating your hidden buffer goes, read the geben help -- there are keyboard shortcuts to finding your current stack location.

Personally, I'm an Emacs *NUT* but when it comes to xdebug I'll fall back on Eclipse. Debugging is such a heavy, precise, multi-windowed task -- and the only time I prefer a mouse to keyboard shortcuts -- and Emacs (and Vim from what I've seen) just doesn't do it comfortably. Use your heavy IDE (Eclipse, Netbeans) for heavy tasks like debugging, your light IDE (Emacs, Vim) for light tasks.

That's a reasonable idea. However I'm often remotely logged into a server that's not my desktop. So I'm living in emacs to get anything done.

My main problem with geben is that it gets into a state where it won't debug a request because it's convinced that it is already debugging. geben-stop, geben-quit... no command will stop it. I have to kill process buffers until I get the right one, then run geben again. Doing that forgets my breakpoints.

I spent a little time making my geben experience a little better. That work may be useful to other emacs users. See details on my blog.

I started project http://drupal.org/project/emacs for merging tools and recipes for PHP and Drupal under Emacs. Current code of the project based on recipes from this page, further plans are:

  • Drupal code navigation with accordance to hooks and site structure (need dig `cedet` and `ecb`)
  • convenience way to browse Drupal docs for functions and hooks
  • set of snippets for Drupal hooks and mostly used functions (through `yasnippet`)
  • ready to use integration with useful development tools (syntax checking, debugger, tags etc.)

--
Axel,
JabberID: axel@drupal.ru
Drupal in Russia

Theerrrre we go, that's what I've been waiting for. And an excellent project it is.

After using emacs-starter-kit for a while and getting tired of all the cruft, I decided to start from scratch with a clean .emacs.d, which I've posted to Github. In that process, I created a separate drupal-mode project which is a simple extension that depends on ejmr's php-mode. It's all currently tested against Emacs 24 pre-release, but I believe it should work on 23.

@axel, your project looks quite interesting, and I intend to spend some time following up on it later.

Users of emacs and coder module might find this helpful.

http://www.dave-cohen.com/node/1000006

Feel free to suggest improvements.

web-mode.el (available on http://web-mode.org) can be used to edit mixed web templates (HTML with PHP/CSS/JS).

(require 'web-mode)
(add-to-list 'auto-mode-alist '("\\.tpl\\.php$" . web-mode))

Hi,

I've blogged about how to set up Emacs with the excellent Drupal mode here:

http://www.freewayprojects.com/2011/12/for-all-drupal-devlopers-who-use-...

Cheers,

bailey86