Skip to content

Add php-mode-maybe and php-project-php-file-as-template #532

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
May 26, 2019
3 changes: 2 additions & 1 deletion php-mode-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,8 @@ an error."
style from Drupal."
(dolist (mode '(pear wordpress symfony2))
;; the file written to has no significance, only the buffer
(let ((tmp-filename (concat (make-temp-name temporary-file-directory) ".php")))
(let ((tmp-filename (concat (make-temp-name temporary-file-directory) ".php"))
(auto-mode-alist '(("\\.php\\'" . php-mode))))
(with-php-mode-test ("issue-53.php")
(search-forward "return $this->bar;")
(should (equal (list "before-write-file" mode nil)
Expand Down
20 changes: 3 additions & 17 deletions php-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -1600,23 +1600,9 @@ The output will appear in the buffer *PHP*."
(ad-activate 'fixup-whitespace)

;;;###autoload
(add-to-list 'auto-mode-alist
(cons
(eval-when-compile
(rx (or
;; File name extensions (ex. "*.php", "*.phtml")
(: "."
(or (: "php" (? (in "s345t")))
"amk"
"phtml"))
;; Full file names (ex. "/Makefile", "/Amkfile")
(: "/"
(or "Amkfile"
".php_cs"
".php_cs.dist")))
string-end))
'php-mode)
t)
(progn
(add-to-list 'auto-mode-alist '("/\\.php_cs\\(?:\\.dist\\)?\\'" . php-mode))
(add-to-list 'auto-mode-alist '("\\.\\(?:php[s345]?\\|phtml\\)\\'" . php-mode-maybe)))

(provide 'php-mode)
;;; php-mode.el ends here
90 changes: 53 additions & 37 deletions php-project.el
Original file line number Diff line number Diff line change
Expand Up @@ -102,85 +102,90 @@ STRING
If the string is an actual directory path, it is set as the absolute path
of the root directory, not the marker.")
(put 'php-project-root 'safe-local-variable
#'(lambda (v) (or (stringp v) (assq v php-project-available-root-files)))))
#'(lambda (v) (or (stringp v) (assq v php-project-available-root-files))))

;;;###autoload
(progn
(defvar-local php-project-bootstrap-scripts nil
"List of path to bootstrap php script file.

The ideal bootstrap file is silent, it only includes dependent files,
defines constants, and sets the class loaders.")
(put 'php-project-bootstrap-scripts 'safe-local-variable #'php-project--eval-bootstrap-scripts))
(put 'php-project-bootstrap-scripts 'safe-local-variable #'php-project--eval-bootstrap-scripts)

;;;###autoload
(progn
(defvar-local php-project-php-executable nil
"Path to php executable file.")
(put 'php-project-php-executable 'safe-local-variable
#'(lambda (v) (and (stringp v) (file-executable-p v)))))
#'(lambda (v) (and (stringp v) (file-executable-p v))))

;;;###autoload
(progn
(defvar-local php-project-phan-executable nil
"Path to phan executable file.")
(put 'php-project-phan-executable 'safe-local-variable #'php-project--eval-bootstrap-scripts))
(put 'php-project-phan-executable 'safe-local-variable #'php-project--eval-bootstrap-scripts)

;;;###autoload
(progn
(defvar-local php-project-coding-style nil
"Symbol value of the coding style of the project that PHP major mode refers to.

Typically it is `pear', `drupal', `wordpress', `symfony2' and `psr2'.")
(put 'php-project-coding-style 'safe-local-variable #'symbolp))
(put 'php-project-coding-style 'safe-local-variable #'symbolp)

;;;###autoload
(progn
(defvar php-project-repl nil
(defvar-local php-project-php-file-as-template 'auto
"
`auto' (default)
Automatically switch to mode for template when HTML tag detected in file.

`t'
Switch all PHP files in that directory to mode for HTML template.

`nil'
Any .php in that directory is just a PHP script.

\(\(PATTERN . SYMBOL))
Alist of file name pattern regular expressions and the above symbol pairs.
PATTERN is regexp pattern.
")
(put 'php-project-php-file-as-template 'safe-local-variable #'php-project--validate-php-file-as-template)

(defvar-local php-project-repl nil
"Function name or path to REPL (interactive shell) script.")
(make-variable-buffer-local 'php-project-repl)
(put 'php-project-repl 'safe-local-variable
#'(lambda (v) (or (functionp v)
(php-project--eval-bootstrap-scripts v)))))
(php-project--eval-bootstrap-scripts v))))

;;;###autoload
(progn
(defvar php-project-unit-test nil
(defvar-local php-project-unit-test nil
"Function name or path to unit test script.")
(make-variable-buffer-local 'php-project-unit-test)
(put 'php-project-unit-test 'safe-local-variable
#'(lambda (v) (or (functionp v)
(php-project--eval-bootstrap-scripts v)))))
(php-project--eval-bootstrap-scripts v))))

;;;###autoload
(progn
(defvar php-project-deploy nil
(defvar-local php-project-deploy nil
"Function name or path to deploy script.")
(make-variable-buffer-local 'php-project-deploy)
(put 'php-project-deploy 'safe-local-variable
#'(lambda (v) (or (functionp v)
(php-project--eval-bootstrap-scripts v)))))
(php-project--eval-bootstrap-scripts v))))

;;;###autoload
(progn
(defvar php-project-build nil
(defvar-local php-project-build nil
"Function name or path to build script.")
(make-variable-buffer-local 'php-project-build)
(put 'php-project-build 'safe-local-variable
#'(lambda (v) (or (functionp v)
(php-project--eval-bootstrap-scripts v)))))
(php-project--eval-bootstrap-scripts v))))

;;;###autoload
(progn
(defvar php-project-server-start nil
(defvar-local php-project-server-start nil
"Function name or path to server-start script.")
(make-variable-buffer-local 'php-project-server-start)
(put 'php-project-server-start 'safe-local-variable
#'(lambda (v) (or (functionp v)
(php-project--eval-bootstrap-scripts v)))))


;; Functions
(defun php-project--validate-php-file-as-template (val)
"Return T when `VAL' is valid list of safe ."
(cond
((null val) t)
((memq val '(t auto)) t)
((listp val)
(cl-loop for v in val
always (and (consp v)
(stringp (car v))
(php-project--validate-php-file-as-template (cdr v)))))
(t nil)))

(defun php-project--eval-bootstrap-scripts (val)
"Return T when `VAL' is valid list of safe bootstrap php script."
Expand Down Expand Up @@ -213,6 +218,17 @@ Typically it is `pear', `drupal', `wordpress', `symfony2' and `psr2'.")
(cons 'root "vendor/bin/phan"))))
(executable-find "phan")))

(defun php-project-get-file-html-template-type (filename)
"Return symbol T, NIL or `auto' by `FILENAME'."
(cond
((not php-project-php-file-as-template) nil)
((eq t php-project-php-file-as-template) t)
((eq 'auto php-project-php-file-as-template) 'auto)
((listp php-project-php-file-as-template)
(assoc-default filename php-project-php-file-as-template #'string-match-p))
(t (prog1 nil
(warn "php-project-php-file-as-template is unexpected format")))))

;;;###autoload
(defun php-project-get-bootstrap-scripts ()
"Return list of bootstrap script."
Expand Down
84 changes: 84 additions & 0 deletions php.el
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

;;; Code:
(require 'flymake)
(require 'php-project)

;;;###autoload
(defgroup php nil
Expand Down Expand Up @@ -85,6 +86,41 @@ You can replace \"en\" with your ISO language code."
"Suffix for inserted namespace."
:group 'php
:type 'string)

(defcustom php-default-major-mode 'php-mode
"Major mode for editing PHP script."
:group 'php
:tag "PHP Default Major Mode"
:type 'function)

(defcustom php-html-template-major-mode 'web-mode
"Major mode for editing PHP-HTML template."
:group 'php
:tag "PHP-HTML Template Major Mode"
:type 'function)

(defcustom php-blade-template-major-mode 'web-mode
"Major mode for editing Blade template."
:group 'php
:tag "PHP Blade Template Major Mode"
:type 'function)

(defcustom php-template-mode-alist
`(("\\.blade" . ,php-blade-template-major-mode)
("\\.phpt\\'" . ,(if (fboundp 'phpt-mode) 'phpt-mode php-html-template-major-mode))
("\\.phtml\\'" . ,php-html-template-major-mode))
"Automatically use another MAJOR-MODE when open template file."
:group 'php
:tag "PHP Template Mode Alist"
:type '(alist :key-type regexp :value-type function)
:link '(url-link :tag "web-mode" "http://web-mode.org/")
:link '(url-link :tag "phpt-mode" "https://github.com/emacs-php/phpt-mode"))

(defcustom php-mode-maybe-hook nil
"List of functions to be executed on entry to `php-mode-maybe'."
:group 'php
:tag "PHP Mode Maybe Hook"
:type 'hook)

;;; PHP Keywords
(defconst php-magical-constants
Expand Down Expand Up @@ -199,6 +235,54 @@ Look at the `php-executable' variable instead of the constant \"php\" command."
'flymake-php-init)))))
(list php-executable (cdr init))))

(defconst php-re-detect-html-tag
(eval-when-compile
(rx (or (: string-start (* (in space))
"<!"
(or "DOCTYPE" "doctype")
(+ (in space))
(or "HTML" "html"))
(: (or line-start
(: "<" (? "/")
(* (in space)) (+ (in alpha "-")) (* (in space)) ">"))
(: "<" (* (in space)) (+ (in alpha "-")) (* (in space)) ">"))))))

(defun php-buffer-has-html-tag ()
"Return position of HTML tag or NIL in current buffer."
(save-excursion
(save-restriction
(widen)
(goto-char (point-min))
(re-search-forward php-re-detect-html-tag nil t))))

(defun php-derivation-major-mode ()
"Return major mode for PHP file by file-name and its content."
(let ((mode (assoc-default buffer-file-name
php-template-mode-alist
#'string-match-p))
type)
(when (and (null mode) buffer-file-name
php-project-php-file-as-template)
(setq type (php-project-get-file-html-template-type buffer-file-name))
(cond
((eq t type) (setq mode php-html-template-major-mode))
((eq 'auto type)
(when (php-buffer-has-html-tag)
(setq mode php-html-template-major-mode)))))
(when (and mode (not (fboundp mode)))
(if (string-match-p "\\.blade\\." buffer-file-name)
(warn "php-mode is NOT support blade template. %s"
"Please install `web-mode' package")
(setq mode nil)))
(or mode php-default-major-mode)))

;;;###autoload
(defun php-mode-maybe ()
"Select PHP mode or other major mode."
(interactive)
(run-hooks php-mode-maybe-hook)
(funcall (php-derivation-major-mode)))

;;;###autoload
(defun php-current-class ()
"Insert current class name if cursor in class context."
Expand Down