diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..253bcb76 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: daily diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6432169b..d3f9e107 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,41 +5,52 @@ on: paths-ignore: - '**/*.md' - 'etc/*' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: build: - runs-on: ubuntu-latest - timeout-minutes: 3 + runs-on: ${{ matrix.os }} + continue-on-error: ${{ matrix.experimental }} strategy: + fail-fast: false matrix: - emacs_version: - - "25.2" - - "25.3" - - "26.1" - - "26.2" - - "26.3" - - "27.1" + os: [ubuntu-latest, macos-latest, windows-latest] + emacs-version: - "27.2" - - "28.1" - - snapshot + - "28.2" + - "29.4" + - "30.1" + experimental: [false] include: - - emacs_version: snapshot - allow_failure: true + - os: ubuntu-latest + emacs-version: snapshot + experimental: true + - os: macos-latest + emacs-version: snapshot + experimental: true + - os: windows-latest + emacs-version: snapshot + experimental: true + exclude: + - os: macos-latest + emacs-version: "27.2" steps: - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v5 with: - python-version: '3.6' + python-version: '3.11' architecture: 'x64' - - uses: purcell/setup-emacs@master + - uses: jcs090218/setup-emacs@master with: - version: ${{ matrix.emacs_version }} - - uses: conao3/setup-cask@master + version: ${{ matrix.emacs-version }} + - uses: emacs-eask/setup-eask@master with: version: 'snapshot' - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Run tests - if: matrix.allow_failure != true - run: 'make .cask test' + run: 'make .eask test' - name: Run tests (allow failure) - if: matrix.allow_failure == true - run: 'make test || true' + run: 'make .eask test || true' diff --git a/.gitignore b/.gitignore index 61be6c11..3e9846c7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,10 @@ -/.cask/ -/.keg/ -php-mode-autoloads.el -*~ *.elc +*~ +.dir-locals-2.el +/.cask/* +/.eask/* +/.keg/* +/dist +/.php-cs-fixer.cache +php-mode-autoloads.el +php_manual_en.json diff --git a/.mailmap b/.mailmap index a2d4abff..1c9c3d6f 100644 --- a/.mailmap +++ b/.mailmap @@ -1,5 +1,6 @@ Elis Axelsson Elis David Arroyo Menéndez David Arroyo7 +Jen-Chieh Shen JenChieh Joe Wreschnig Joe Jon Dufresne Jon Dufrense Syohei YOSHIDA Shohei YOSHIDA diff --git a/AUTHORS.md b/AUTHORS.md index a20522f0..6aceea50 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -24,6 +24,7 @@ Names Sorted Alphabetically: - Aaron S. Hawley - Alan Pearce +- Alex Figl-Brick - Andreas Röhler - Andrei Chițu - Antoine Brand @@ -62,6 +63,7 @@ Names Sorted Alphabetically: - Jakub Jankiewicz - James Laver - Jeff Beeman +- Jen-Chieh Shen - Joe Wreschnig - John Keller - Jon Dufresne @@ -82,7 +84,9 @@ Names Sorted Alphabetically: - Norio Suzuki - Olaf The Viking - Peter Oliver +- Phil Sainty - Philippe Ivaldi +- Piotr Kwiecinski - Rex McMaster - Roland - Rosenfeld @@ -91,6 +95,7 @@ Names Sorted Alphabetically: - Sean Champ - Sebastian Wiesner - Serghei Iakovlev +- Shohei YOSHIDA - Stefan Monnier - Stig Bakken - Syohei YOSHIDA @@ -108,6 +113,7 @@ Names Sorted Alphabetically: - l3msh0 - phil-s - ppercot +- take - takeokunn - tangxinfa - tetsujin diff --git a/CHANGELOG.md b/CHANGELOG.md index 2da0a407..115ef78d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,243 @@ All notable changes of the PHP Mode 1.19.1 release series are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles. - +## Unreleased + +### Changed + + * Add `readonly` class modifier to [Imenu] ([#802]) + * Add `enum` support to `php-current-class` ([#802]) + * Remove hardcoding of implicit paths in `php` that are not guaranteed to exist ([#803]) + +[Imenu]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Imenu.html +[#802]: https://github.com/emacs-php/php-mode/pull/802 +[#803]: https://github.com/emacs-php/php-mode/pull/803 + +## [1.27.0] - 2024-12-20 + +### Added + + * Support PHP 8.4 property-hooks ([#797]) + +### Changed + + * Improve `php-syntax-propertize-extend-region` efficiency ([#789], thanks [@phil-s]!) + * Update `php-phpdoc-type-names` to support [PHPStan 2.0.4] ([#795]) + +### Fixed + + * Fix Emacs 30 byte-compile errors ([#792]) + * Use `when-let*` instead of `when-let` to enhance Emacs 30 compatibility ([#796]) + +### Removed + + * Drop support for Emacs 26 ([#788]) + +[@phil-s]: https://github.com/phil-s +[PHPStan 2.0.4]: https://github.com/phpstan/phpstan/releases/tag/2.0.4 +[#788]: https://github.com/emacs-php/php-mode/pull/788 +[#789]: https://github.com/emacs-php/php-mode/pull/789 +[#792]: https://github.com/emacs-php/php-mode/pull/792 +[#795]: https://github.com/emacs-php/php-mode/pull/795 +[#796]: https://github.com/emacs-php/php-mode/pull/796 +[#797]: https://github.com/emacs-php/php-mode/pull/797 + +## [1.26.1] - 2024-09-13 + +### Added + + * Add `php-base-mode` which is the base of php related modes ([#772]) + * `php-base-mode` is designed as a common parent mode for `php-mode` ~~and [`php-ts-mode`](https://github.com/emacs-php/php-ts-mode)~~. + +### Changed + + * Make `php-mode` inherit from `php-base-mode` instead of `c-mode` ([#772]) + * Modify indentation of [PEAR Coding Standards] ([#774], [#777]) + * No longer overindent it by default, since we don't see any mention in the coding style that it should hang `.`. (refs [#227] and [#229]) + * **If you have any feedback on PEAR style, please let us know in [the discussion #776][#776].** + * Remove `$` from face names for interoperability with treesit ([#780], [emacs-php/php-ts-mode#68]) + * `php-$this` → `php-this` + * `php-$this-sigil` → `php-this-sigil` + * Add `php-function-call-standard` face inherit `font-lock-function-call-face` on Emacs 29.1 and above ([#782], thanks [@bricka]!) + * Add `-tranditional` suffix to the `php-*-call` faces. + * `php-function-call` → `php-function-call-traditional` + * `php-method-call` → `php-method-call-traditional` + * `php-static-method-call` → `php-static-method-call-traditional` + * Add variables for the `php-function-call`, `php-method-call`, and `php-static-method-call` faces, defaulting to the `-traditional` face. + * Changes how php-syntax-propertize-rules are applied for the first time. ([#785] and [#786]) + * This change is expected to make heredoc and attribute coloring more stable and reduce flicker. + +### Removed + + * Remove `php-mode-disable-c-mode-hook` custom variable and `php-mode-neutralize-cc-mode-effect` function ([#775]) + * `php-mode` no longer inherits `c-mode`, so this variable won't work. + +[#227]: https://github.com/emacs-php/php-mode/pull/227 +[#229]: https://github.com/emacs-php/php-mode/pull/229 +[#772]: https://github.com/emacs-php/php-mode/pull/772 +[#774]: https://github.com/emacs-php/php-mode/issues/774 +[#775]: https://github.com/emacs-php/php-mode/pull/775 +[#776]: https://github.com/emacs-php/php-mode/discussions/776 +[#777]: https://github.com/emacs-php/php-mode/pull/777 +[#780]: https://github.com/emacs-php/php-mode/issues/780 +[#782]: https://github.com/emacs-php/php-mode/issues/782 +[#785]: https://github.com/emacs-php/php-mode/issues/785 +[#786]: https://github.com/emacs-php/php-mode/pull/786 +[@bricka]: https://github.com/bricka +[emacs-php/php-ts-mode#68]: https://github.com/emacs-php/php-ts-mode/pull/68 +[PEAR Coding Standards]: https://pear.php.net/manual/en/standards.php + +## [1.26.0] - 2024-09-13 + +> [!NOTE] +> This version was cancelled due to a release error. + +## [1.25.1] - 2023-11-24 + +### Added + + * Add `php-topsy-beginning-of-defun-with-class` to display classname with function signature. ([#766]) + * Add missing `__DIR__` to `php-magical-constants` ([#756], thanks [@piotrkwiecinski]) + +### Changed + + * Make developer build task in Makefile now depends on Eask. ([#762], thanks [@jcs090218]) + * This change does not affect package installation users + * Read [CONTRIBUTING.md] if you prefer to build it yourself from zip or tar ball + +### Fixed + + * Fixed build failure in Emacs on master branch ([#764], [#765], [#767], thanks [@takeokunn]) + +### Removed + + * Removed Phan-specific features from `php-project` ([#754]) + * Removed [Cask](https://cask.readthedocs.io/) and [Keg](https://github.com/conao3/keg.el) metadata files for building ([#770]) + +[#754]: https://github.com/emacs-php/php-mode/pull/754 +[#756]: https://github.com/emacs-php/php-mode/pull/756 +[#762]: https://github.com/emacs-php/php-mode/pull/762 +[#764]: https://github.com/emacs-php/php-mode/issues/764 +[#765]: https://github.com/emacs-php/php-mode/pull/765 +[#766]: https://github.com/emacs-php/php-mode/pull/766 +[#767]: https://github.com/emacs-php/php-mode/pull/767 +[#770]: https://github.com/emacs-php/php-mode/pull/770 +[@jcs090218]: https://github.com/jcs090218 +[@piotrkwiecinski]: https://github.com/piotrkwiecinski +[@takeokunn]: https://github.com/takeokunn +[CONTRIBUTING.md]: https://github.com/emacs-php/php-mode/blob/master/CONTRIBUTING.md + +## [1.25.0] - 2023-07-24 + +### Added + + * **Support Emacs 29.1** ([#743], [#750]) + +### Fixed + + * Fixed many byte compilation errors on Emacs 29 and 30 by multiple patches contributed by [Stefan Monnier] ([#737], [#739] and [#740], thanks Stefan!) + * Fixed PEAR method chaining wrong indentation ([#745] and [#746], thanks [@cweiske]!) + * Fixed `php-mode-debug-reinstall` command ([#747], [#748]) + +### Removed + + * Drop Emacs 25 support ([#729], [736]) + +[Stefan Monnier]: https://www.iro.umontreal.ca/~monnier/ +[#729]: https://github.com/emacs-php/php-mode/pull/729 +[#736]: https://github.com/emacs-php/php-mode/pull/736 +[#737]: https://github.com/emacs-php/php-mode/pull/737 +[#739]: https://github.com/emacs-php/php-mode/pull/739 +[#740]: https://github.com/emacs-php/php-mode/pull/740 +[#741]: https://github.com/emacs-php/php-mode/pull/741 +[#743]: https://github.com/emacs-php/php-mode/pull/743 +[#745]: https://github.com/emacs-php/php-mode/pull/745 +[#746]: https://github.com/emacs-php/php-mode/pull/746 +[#747]: https://github.com/emacs-php/php-mode/pull/747 +[#748]: https://github.com/emacs-php/php-mode/pull/748 +[#750]: https://github.com/emacs-php/php-mode/pull/750 + +## [1.24.3] - 2023-03-19 + +### Added + + * **Net feature**: `php-format` ([#731]) + * Add `php-format-project` and `php-format-this-buffer-file` commands + * Add `php-format-auto-mode` minor mode + * **Experimental feature: `php-ide`** ([#709]) + * Add `php-ide-phpactor` as simple IDE feature without LSP clients + * Add `php-ide-mode` minor mode for binding IDE-like features + +### Fixed + + * Fix array indentation broken by commenting out ([#726], [#732]) + +### Removed + + * No longer highlights `'link` in PHPDoc ([#724]) + * Please use `goto-address-prog-mode` minor mode + +[#709]: https://github.com/emacs-php/php-mode/pull/709 +[#724]: https://github.com/emacs-php/php-mode/pull/724 +[#726]: https://github.com/emacs-php/php-mode/pull/726 +[#731]: https://github.com/emacs-php/php-mode/pull/731 +[#732]: https://github.com/emacs-php/php-mode/pull/732 + +## [1.24.2] - 2022-11-13 + +### Added + + * **New feature: `php-complete`** + * Add `php-complete-complete-function` to autocomplete function names ([#708]) + * **New feature: `php-flymake`** + * Add `php-flymake` as a flymake backend compatible with Emacs 26 and above ([#718]) + * Supports PHPDoc tags and types for static analysis tools ([#710], [#715], [#716], [#717], thanks to [@takeokunn]) + * Please refer to the article below + * PHPStan: [PHPDoc Types](https://phpstan.org/writing-php-code/phpdoc-types) + * PHPStan: [PHPDocs Basics](https://phpstan.org/writing-php-code/phpdocs-basics) + * Psalm: [Atomic Type Reference](https://psalm.dev/docs/annotating_code/type_syntax/atomic_types/) + * Psalm: [Supported Annotations](https://psalm.dev/docs/annotating_code/supported_annotations/) + * Psalm: [Template Annotations](https://psalm.dev/docs/annotating_code/templated_annotations/) + * Add `php-mode-replace-flymake-diag-function` custom variable and default activated it ([#718]) + * Add `php-mode-debug-reinstall` command to help users who update Emacs themselves ([#721]) + +### Changed + + * Make continued expressions inside lists (arguments and arrays, etc.) have the same indent width as outside the list ([#703]) + * (internal) Improved readability of test failures about indentation ([#707]) + * `php-doc-annotation-tag` inherits `font-lock-doc-markup-face` if defined in Emacs 28 ([#711]) + * Make `php-mode-version` function include a Git tag and revision ([#713]) + * Like `"1.23.4-56-xxxxxx"` for example. + * Change `php-phpdoc-type-keywords` to `php-phpdoc-type-names` to avoid confusion ([#717]) + * Make `php-flymake-php-init` append to `flymake-allowed-file-name-masks` only in legacy Flymake ([#718]) + +### Deprecated + + * Make obsolete `php-mode-version-number` contstant variable ([#712]) + * `(php-mode-version :as-number t)` is provided for use cases comparing as versions, but generally SHOULD NOT be dependent on the PHP Mode version. + * Make obsolete `php-mode-disable-c-mode-hook` customize variable ([#718]) + +### Removed + + * Remove `php-mode-disable-c-auto-align-backslashes` as it doesn't make sense and is always disabled + +### Fixed + + * Removed invalid definitions that caused errors in some expressions ([#704]) + +[#703]: https://github.com/emacs-php/php-mode/pull/703 +[#704]: https://github.com/emacs-php/php-mode/pull/704 +[#707]: https://github.com/emacs-php/php-mode/pull/707 +[#708]: https://github.com/emacs-php/php-mode/pull/708 +[#710]: https://github.com/emacs-php/php-mode/pull/710 +[#711]: https://github.com/emacs-php/php-mode/pull/711 +[#713]: https://github.com/emacs-php/php-mode/pull/713 +[#715]: https://github.com/emacs-php/php-mode/pull/715 +[#716]: https://github.com/emacs-php/php-mode/pull/716 +[#717]: https://github.com/emacs-php/php-mode/pull/717 +[#718]: https://github.com/emacs-php/php-mode/pull/718 +[#719]: https://github.com/emacs-php/php-mode/pull/719 +[#721]: https://github.com/emacs-php/php-mode/pull/721 ## [1.24.1] - 2022-10-08 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f7c36c85..58c3d13b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,19 +11,25 @@ This project accepts contributions in various languages. Not only development b ## English +### Setup (en) + +[Eask] is required to build the package. You need to install it using node.js and npm, or [download a prebuilt executable][eask-releases]. See [Eask Introduction][eask-introduction] and [Install Eask][eask-install] for more details. + +You can run the tests using the following command: + +```sh +make test +``` + ### Guideline (en) -All contributions to PHP Mode are welcome. But please try to do the following when sending improvements or bug fixes: +All contributions to PHP Mode are welcome. But please try to do the following when submitting enhancements or bug fixes: - 1. Add your name to the list of ‘Contributors’ in this `README.md` file if it is not there already. If you have a GitHub page and/or personal site then please feel free to link your name to it so people can see your other work. - 2. If your contribution addresses an issue on the GitHub project page then include a single line like `GitHub-Issue: #16` with the appropriate issue number. - 3. Make sure to update the constant `php-mode-modified` *only if you patch affects `php-mode.el`,* which means this step is unnecessary for patches related to unit tests. - 4. However, please do not modify `php-mode-version-number`. The maintainers will decide what constitutes a bump in the version number. - 5. Open the `php-mode-test.el` file and [run all of the tests] to ensure they still pass as expected. Sometimes we expect for a test to fail, and those unit tests have the appropriate configuration so their failure will not raise any warnings. You can use `make test` script to run all tests from a terminal, which is also useful in conjunction with [`git bisect run`]. - 6. Send us a pull request here on GitHub. - 7. Please make your commit messages as detailed as possible. It is better to be too verbose than to write too little. Look at the commits of the maintainers to see many examples of the level of detail that we feel is ideal. Please never assume that your patch is so simple that future developers will be able to understand the *reason* for the change without comment. And that is important: your commit message should always strive to answer *"Why"* the patch exists, *"What*" does it accomplish? The maintainers will sometimes write detailed commit messages for pull-requests by other developers, but please do not rely on us to do this consistently. + 1. Open `php-mode-test.el` and run all the tests to make sure they pass as expected. You can also check this by running `make test` from the terminal. + 2. Please commit with a concise and clear message that effectively achieves a simple purpose. + 3. Submit a pull request here on GitHub. -If you are fixing a bug related to a GitHub issue, then first of all, thank you for the help improving PHP Mode. Second, there is a `tests/` directory which contains PHP scripts for issues (although not all of them). Please consider adding a test script to that directory that documents the expected behavior and provides code that allows others to see if said behavior works properly. Then create a unit test within `php-mode-test.el` using [ERT]. Please try to follow the format of the existing tests. +If you are fixing a bug related to a GitHub issue, then first of all, thank you for helping to improve PHP Mode. Second, there is a `tests/` directory that contains PHP scripts for issues (though not all). Please consider adding a test script to that directory that documents the expected behavior and provides code that allows others to see if that behavior works properly. Then create a unit test within `php-mode-test.el' using [ERT]. Try to follow the format of the existing tests. ### Regression test for Face (en) @@ -55,17 +61,23 @@ Please contribute. ## Japanese +### Setup (ja) + +パッケージをビルドするにはEaskが必要です。node.jsとnpmからインストールするか、ビルド済み実行ファイルをダウンロードする必要があります。Easkの詳細は[Eask Introduction][eask-introduction]と[Install Eask][eask-install]をお読みください。 + +以下のコマンドでテストを実行できます。 + +```sh +make test +``` + ### Guideline (ja) **Emacs PHP Mode**はどなたからの貢献も歓迎です。改善やバグ修正を行う前に以下の手順を行ってください。 - 1. あなたの名前が`README.md`の“Contributors”のリストになければ追加してください。あなたの名前とGitHubアカウントや個人サイトをリンクして構いません。 - 2. もし既にissueとして提起された問題に対処するならば、コミットメッセージに`GitHub-Issue: #16`のような行を含めてください。 - 3. `php-mode.el`に影響する変更をした場合、`php-mode-modified`定数を更新してください。テストやドキュメントのみの修正の場合は不要です。 - 4. しかし、 `php-mode-version-number` は変更しないでください。メンテナがバージョンを決定します。 - 5. `php-mode-test.el`を開いて[すべてのテストを実行][run all of the tests]し、期待通りにテストを通過することを確認します。端末から`make test`で確認することもでき、[`git bisect run`]と併用すると便利です。 - 6. GitHubからプルリクエストを送信します - 7. 可能な限り詳細なコミットメッセージを作成してください。不足するよりも冗長すぎる方が良いです。メンテナーのコミットを参照して、私たちが理想的だと期待するコミットメッセージの詳細度として参考にしてください。シンプルすぎるパッチだからと決めつけずにコミットメッセージを書けば、コード中にコメントを書かなくても将来の開発者がコミットの「理由」「経緯」を理解できるようになります。コミットメッセージには「なぜ」コミットを作成したか、「何を」解決するものなのかを記述することが重要です。メンテナーはほかの開発者のプルリクエストに詳細なコミットメッセージを書き込むことがありますが、常に一貫して行われるとは期待しないでください。 + 1. `php-mode-test.el`を開いて[すべてのテストを実行][run all of the tests]し、期待通りにテストを通過することを確認します。端末から`make test`で確認することもできます。 + 2. 簡潔でシンプルな目的を達成するための、明示的なメッセージでコミットしてください。 + 3. GitHubからプルリクエストを送信してください。 **GitHubのissueに関連するバグを修正する場合**: PHPモードの改善に協力いただきありがとうございます! `tests/`ディレクトリには(すべてではありませんが)issueに関連のあるPHPスクリプトが配置されています。そこに予期される挙動と他のひとが動作を適切に確認できるテストコードを追加することを検討してください。そして`php-mode-test.el`に既存のテストと同じように[ERT]を使ったテストコードを追加してください。 @@ -76,7 +88,10 @@ Please contribute. このテストに必要な`.face`ファイルの生成方法は[How to generate face file]を参考にしてください。 [run all of the tests]: http://www.gnu.org/software/emacs/manual/html_node/ert/Running-Tests-Interactively.html#Running-Tests-Interactively -[`git bisect run`]: http://git-scm.com/book/en/Git-Tools-Debugging-with-Git +[Eask]: https://emacs-eask.github.io/ +[eask-introduction]: https://emacs-eask.github.io/Getting-Started/Introduction/ +[eask-install]: https://emacs-eask.github.io/Getting-Started/Install-Eask/ +[eask-releases]: https://github.com/emacs-eask/cli/releases [ERT]: http://www.gnu.org/software/emacs/manual/html_node/ert/index.html [Font Lock]: https://www.gnu.org/software/emacs/manual/html_node/elisp/Font-Lock-Mode.html [How to generate face file]: https://github.com/emacs-php/php-mode/issues/509#issuecomment-491528968 diff --git a/Cask b/Cask deleted file mode 100644 index d992fad3..00000000 --- a/Cask +++ /dev/null @@ -1,17 +0,0 @@ -(package "php-mode" "1.24.0" "Major mode for editing PHP code") -(source melpa) - -(package-file "lisp/php-mode.el") - -(files - "lisp/php.el" - "lisp/php-face.el" - "lisp/php-project.el" - "lisp/php-local-manual.el" - "lisp/php-mode-debug.el") - -(development - (depends-on "pkg-info") - (depends-on "projectile") - (depends-on "smart-jump") - (depends-on "shut-up")) diff --git a/Eask b/Eask new file mode 100644 index 00000000..88e00bf2 --- /dev/null +++ b/Eask @@ -0,0 +1,38 @@ +;; -*- mode: eask; lexical-binding: t -*- + +(package "php-mode" + "1.26.1" + "Major mode for editing PHP code") + +(website-url "https://github.com/emacs-php/php-mode") +(keywords "languages" "php") + +(package-file "lisp/php-mode.el") +(files + "lisp/php.el" + "lisp/php-complete.el" + "lisp/php-defs.el" + "lisp/php-face.el" + "lisp/php-format.el" + "lisp/php-project.el" + "lisp/php-local-manual.el" + "lisp/php-ide-phpactor.el" + "lisp/php-ide.el" + "lisp/php-mode-debug.el") + +(script "test" "echo \"Error: no test specified\" && exit 1") + +(source 'melpa) +(source 'gnu) + +(depends-on "emacs" "26.1") + +(development + (depends-on "phpactor") + (depends-on "pkg-info") + (depends-on "projectile") + (depends-on "smart-jump") + (depends-on "shut-up") + ) + +(setq network-security-level 'low) ; see https://github.com/jcs090218/setup-emacs-windows/issues/156#issuecomment-932956432 diff --git a/Keg b/Keg deleted file mode 100644 index f08e89ed..00000000 --- a/Keg +++ /dev/null @@ -1,10 +0,0 @@ -(source gnu melpa) - -(package - (php-mode - (recipe . (php-mode :fetcher github :repo "emacs-php/php-mode.el" - :files (:defaults "lisp/*.el"))))) - -(dev-dependency pkg-info) -(dev-dependency projectile) -(dev-dependency shut-up) diff --git a/LICENSE b/LICENSE index 94a9ed02..f288702d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found. GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program. If not, see . + along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. @@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see -. +. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. +. diff --git a/Makefile b/Makefile index 2bad67e0..9b722e9b 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,9 @@ EMACS ?= emacs CASK ?= cask -ELS = lisp/php.el lisp/php-align.el lisp/php-face.el lisp/php-project.el lisp/php-local-manual.el lisp/php-mode.el lisp/php-mode-debug.el -AUTOLOADS = php-mode-autoloads.el -ELCS = $(ELS:.el=.elc) +EASK ?= eask -%.elc: %.el - $(EMACS) -Q -batch -L lisp/ -f batch-byte-compile $< +compile: + $(EASK) compile all: autoloads $(ELCS) authors @@ -16,25 +14,22 @@ AUTHORS.md: etc/git/AUTHORS.md.in .mailmap @printf "Generating AUTHORS.md file..." @test -d .git \ && (cat $< > $@ \ - && git log --pretty=format:'- %aN' | \ - cat etc/git/former-contributors - | LANG=C sort -u >> $@ \ + && git log --pretty=format:'- %aN' \ + | cat etc/git/former-contributors - \ + | grep -v dependabot \ + | LANG=C sort -u >> $@ \ && cat etc/git/AUTHORS2.md.in >> $@ \ && printf "FINISHED\n" ; ) \ || printf "FAILED (non-fatal)\n" -autoloads: $(AUTOLOADS) +autoloads: + $(EASK) generate autoloads -$(AUTOLOADS): lisp/php.el lisp/php-align.el lisp/php-face.el lisp/php-project.el lisp/php-local-manual.el lisp/php-mode-debug.el lisp/php-mode.el - $(EMACS) -Q -batch -L lisp/ --eval \ - "(progn \ - (require 'package) \ - (package-generate-autoloads \"php-mode\" (expand-file-name \"lisp\")))" - -.cask: Cask - $(CASK) install +.eask: Eask + $(EASK) install clean: - rm -f $(ELCS) $(AUTOLOADS) + $(EASK) clean all # Perform any operations that will be useful for developers # who contribute to PHP Mode. @@ -54,12 +49,6 @@ dev: # for an example of using a script like this with the 'git bisect run' # command. test: clean all - touch tests/project/1/.git - $(EMACS) -Q -batch -L lisp/ --eval \ - "(let ((default-directory (expand-file-name \".cask\" default-directory))) \ - (require 'package) \ - (normal-top-level-add-subdirs-to-load-path))" \ - -f package-initialize \ - -l tests/php-mode-test.el -f ert-run-tests-batch-and-exit + $(EASK) test ert ./tests/php-mode-test.el .PHONY: all authors autoloads clean test diff --git a/README.ja.md b/README.ja.md index ee1fcf0f..81c0f510 100644 --- a/README.ja.md +++ b/README.ja.md @@ -1,79 +1,73 @@

Emacs PHP Mode

-[![Emacs: 28.1](https://img.shields.io/badge/Emacs-28.1-blue.svg)](https://www.gnu.org/software/emacs/) -[![lang: PHP 8.1](https://img.shields.io/badge/lang-PHP%208.1-brightgreen.svg)](https://php.net/manual/migration81.php) -[![lang: PHP 7](https://img.shields.io/badge/lang-PHP%207-green.svg)](https://php.net/downloads.php) +[![Emacs: 30.0](https://img.shields.io/badge/Emacs-30.0-blue.svg)](https://www.gnu.org/software/emacs/) +[![lang: PHP 8.4](https://img.shields.io/badge/lang-PHP%208.4-brightgreen.svg)](https://www.php.net/releases/8.4/) [![Build Status](https://github.com/emacs-php/php-mode/workflows/CI/badge.svg)](https://github.com/emacs-php/php-mode/actions) -[![GPL v3](https://img.shields.io/badge/license-GPL_v3-green.svg)](http://www.gnu.org/licenses/gpl-3.0.txt)
+[![GPL v3](https://img.shields.io/badge/license-GPL_v3-green.svg)][gpl-v3]
[![NonGNU ELPA][nongnu-elpa-badge]][nongnu-elpa] [![melpa badge][melpa-badge]][melpa-link] A powerful and flexible Emacs major mode for editing PHP scripts -
- -[PHP ModeのGitHubプロジェクト][php-mode]にissueを作成してバグ報告や機能リクエストを送ってください。 - -インストール ------------- - -**PHP ModeはEmacs 25.2以降で動作します**。古いバージョンのEmacsでも動作するかもしれませんが、保証外です。 古いバージョンのEmacsのPHPモードを使用することによる問題のバグ報告は積極的に対応しません。現在のサポートポリシーは[Supported Version]のページをご覧ください。 - -### **(推奨)** NonGNU ELPAからのインストール - -[![NonGNU ELPA][nongnu-elpa-badge]][nongnu-elpa] [![NonGNU-devel ELPA][nongnu-devel-elpa-badge]][nongnu-devel-elpa] +[English](README.md)   |   日本語 -Emacs 28 (最新安定版) では[NonGNU ELPA](https://elpa.nongnu.org/)がデフォルトのパッケージリポジトリとして追加されています。 - - -### MELPAからのインストール - -[![melpa badge][melpa-badge]][melpa-link] [![melpa stable badge][melpa-stable-badge]][melpa-stable-link] + -GNU Emacs 24以降では、[package][]機能(または[Cask][])を使って[MELPA][]/[MELPA Stable][]からPHP Modeをインストールできます。 +[GitHubプロジェクト][php-mode]にissueを作成してバグ報告や機能リクエストを送ってください。 -### OSのパッケージマネージャからのインストール +> [!NOTE] +> [最新版][releases]のPHP ModeはEmacs 30をサポートしています。
アップグレードに伴うトラブルは[Discussions][discussions-emacs30]に気軽に書き込んでください。 -PHP Modeは[いくつかのOSが提供するパッケージシステム][php-mode-packages]からインストール可能です。DebianおよびUbuntuのようなDebian派生のOSではEmacsにPHPを追加する最も簡単な方法で、`sudo apt install elpa-php-mode` で導入できます。これらの "Stable" リリースは最新のPHP Modeよりも古いものの、十分にテストされた固定バージョンが提供されます。オートロードとバイトコンパイルは自動で行われます。 +> [!WARNING] +> Emacsをアップグレードした直後に初めてPHPファイルを開いたときに、CC Mode関連のエラーが発生する可能性があります。これは以前のバージョンのEmacsでバイトコンパイルされたPHP Modeがディスクにキャッシュされているために起こるので、PHP Modeの再インストールによって解決します。 +> +> **`M-x php-mode-debug-reinstall`** または **`M-x package-reinstall php-mode`** コマンドをお試しください。 -最新のPHP Modeの機能とパフォーマンス改善を活用するために**sid** ("unstable"としても知られるローリングリリース)で提供されるバージョンのインストールを検討してください。最新バージョンは[`elpa-php-mode`][elpa-php-mode]にあります。"apt-pinning"を使ってアップデートを自動化することもできます。 +[releases]: https://github.com/emacs-php/php-mode/releases +[discussions-emacs30]: https://github.com/emacs-php/php-mode/discussions/798 -また、[Debian 9 (stretch)][php-elisp-stretch]や[Ubuntu 18.10][php-elisp-ubuntu1810]以前で提供されていた `php-elisp` パッケージは[あまりにも古い][issue-430]ので、 **くれぐれもインストールしないでください**。 +## インストール -### 手動でインストール +**PHP ModeはEmacs 27.1以降で動作します**。対応バージョンの詳細は[Supported Version]をお読みください。Emacs 28以降では単に以下のコマンドを実行するだけでインストールできます。 -もしパッケージマネージャに依存したくなければ、伝統的な方法によってLispファイルを直接インストールすることもできます。詳細なセットアップ方法は[手動でのインストール][wiki-manual-installation-ja]ページをご覧ください。 +``` +M-x package-install php-mode +``` -バグを報告する --------------- +[`package-archives`にMELPAを追加][melpa-getting-started]することで、Web上の多くのパッケージでEmacsを強化できます。 -報告の際には `M-x php-mode-debug` コマンドを実行して、その出力をバグレポートに含めてください。問題を再現するための手がかりになります。 +パッケージマネージャへの依存なしでインストールしたい場合は、Lispファイルを直接配置する伝統的な方法も可能です。詳しくは[Manual installation][wiki-manual-installation]をお読みください。 -Settings --------- +## 設定 ### 個人設定 -.emacsファイル(`~/.emacs.d/init.el`)にPHPモードでの設定を記述できます。 +.emacsファイル(`~/.emacs.d/init.el`)にPHPモードの設定を記述できます。 ```lisp (defun my-php-mode-init () + (subword-mode 1) (setq-local show-trailing-whitespace t) (setq-local ac-disable-faces '(font-lock-comment-face font-lock-string-face)) - (setq-local page-delimiter "\\_<\\(class\\|function\\|namespace\\)\\_>.+$") + (add-hook 'hack-local-variables-hook 'php-ide-turn-on nil t)) - ;; If you feel phumped and phpcs annoying, invalidate them. - (when (boundp 'flycheck-disabled-checkers) +(with-eval-after-load 'php-mode + (add-hook 'php-mode-hook #'my-php-mode-init) + (custom-set-variables + '(php-mode-coding-style 'psr2) + '(php-mode-template-compatibility nil) + '(php-imenu-generic-expression 'php-imenu-generic-expression-simple)) + + ;; If you find phpcs to be bothersome, you can disable it. + (when (require 'flycheck nil) (add-to-list 'flycheck-disabled-checkers 'php-phpmd) (add-to-list 'flycheck-disabled-checkers 'php-phpcs))) - -(add-hook 'php-mode-hook #'my-php-mode-init) ``` ### プロジェクトローカル設定 -プロジェクトのトップディレクトリに`.dir-locals.el`を記述すると、プロジェクト単位の設定を追加することができます。このファイルはユーザー自身のEmacsにインストールされたパッケージに依存するため、バージョン管理の対象に含めないことを推奨します。 +プロジェクトのトップディレクトリに`.dir-locals.el`または`.dir-locals-2.el`を記述すると、プロジェクト単位の設定を追加することができます。このファイルはユーザー自身のEmacsにインストールされたパッケージに依存するため、バージョン管理の対象に含めないことを推奨します。 ```lisp ((nil @@ -81,226 +75,15 @@ Settings (php-project-coding-style . psr2))) ``` -実験的および作業中の機能 -------------------------------------- - -### CC Mode, CEDET, EDE, and Semantic - -In 2013 Daniel Haxney began rewriting parts of PHP Mode in terms of Emacs' built-in CC Mode. This laid the foundation for incorporating some of the inherit IDE-ish features of Emacs, such as CEDET, EDE, and Semantic. Support for these tools continues to improve thanks to the work of Andrea Turso, Steven Rémot, Joris Steyn, and others. If you wish to test, contribute to, or simply experiment with such features then [this thread is a good place to start](https://github.com/emacs-php/php-mode/issues/256). - -### PHP7サポート - -PHP7がリリースされました。PHPモードはPHP7からの以下の文法をサポートします。 - - 1. 返り値の型宣言 - 2. `yield from` キーワード - 3. `declare(strict_types=1)` 宣言 - -機能 --------- - -### 新しいキーワード - -現在のPHPモードはトレイト関連の`insteadof`などのPHP5.4で導入された新しいキーワードを構文強調表示します。また、従来のキーワード`clone`や`default`などもサポートします。 - -### 定数 - -強調表示には公式のPHPマニュアルに記載があるすべてのマジック定数と定義済み定数が含まれます。ただし、特定の拡張機能の定数は現在のところ含みません。 - -### トレイト、インターフェイスと名前空間 - -トレイト、インターフェイス、名前空間がImenuリストに表示されるようになりました。フォント表示は名前空間でも正しく動作するようになり、`namespace Foo\Bar\Baz`のようなコードはもはや警告されません。`use as `のような名前空間のエイリアスも同様です。現在のところエイリアス名はImenuのリストには含まれませんが、将来のバージョンでは対応予定です。 - -### アンダースコアの取り扱い - -PHPモードは`$foo_bar_baz`のような変数名の単語のそれぞれの部分を移動できるように、アンダースコア(`_`)を「シンボル構成要素 (symbol constituents)」(Emacs用語)として取り扱います。 - -### メソッドチェーン呼び出し - -複数行にわたるメソッド呼び出しを`->`の位置に揃えること(アライメント)ができます。 - -```php -$object->foo() - ->bar() - ->baz(); -``` - -この動作はデフォルトでは無効ですが、カスタマイズ変数 `php-mode-lineup-cascaded-calls` をセットすることで有効化できます。 - -**注意**: アライメントは、PHPモードのコーディングスタイルのひとつを使用するか、それを継承した場合のみ機能します。 - -### ネストされた配列の整形 - -ネストされた関数呼び出しと `array()` 構文は現在デフォルトで(少くとも私の意見では)よく見えるようになりました。例として、このようなスタイルです: - -```php -$results = Post::model()->find( - array( - 'select' => 'title', - 'condition' => 'postID=:postID', - 'params' => array(':postID' => 10), - ) -); -``` - -### 無名関数 - -以下のような無名関数 - -```php -$greet = function($name) { ... }; -``` - -これは現在、Imenuで`$greet`として表示します。 - -### Flymakeサポート - -カスタマイズ変数`php-executable`をセットすることで、コーディング中に警告とエラーをリアルタイムで見るためにFlymakeモードを有効にすることができます。 - -### ローカルのドキュメントを検索する - -コマンド`C-c C-f`でカーソル位置のシンボルをPHP公式サイトのドキュメントから検索できます。また、[ローカルにドキュメントをダウンロード](http://us2.php.net/download-docs.php)してあれば、それを優先します。`php-manual-path`をセットするだけです。もしローカルで発見できなければPHPのWebサイトにフォールバックします。 - -### 選択範囲内のコードを実行する - -`php-send-region`コマンド(デフォルトでは`C-c C-r`)はリージョンで選択された範囲のPHPコードを実行します。`C-x h`と組合せてコード全体を実行することもできます。出力は `*PHP*` バッファに現れます。 - -### PHPDoc タグ/アノテーション - -PHPDocは[JavaDoc](https://en.wikipedia.org/wiki/Javadoc)に似たドキュメンテーションの形式です。 - -`@param`, `@return`, `@var`... などの表記は**タグ**と呼ばれ、[list of tags defined by phpDocumentor2](https://phpdoc.org/docs/latest/references/phpdoc/tags/index.html)で定義されます。 (これらのタグはPhpStormや[Phan](https://github.com/etsy/phan)といった型チェッカーと互換性があります。) - -**アノテーション**と呼ばれる記法は部分的にサポートしています。アノテーションの文法はタグとは少し異なり、`@Annotation(attr1="vvv", attr2="zzz")` のような形式です。 - -[Symfony](https://symfony.com/)プロジェクトや[Go! AOP](https://github.com/goaop/framework)などいくつかのプロジェクト・フレームワークは[Doctrine Annotations](https://www.doctrine-project.org/projects/doctrine-annotations/en/latest/index.html)の文法を元にしています。 - -```php -/** - * Summary of Product class - * - * @copyright 2112 John Doe - * @license https://spdx.org/licenses/Apache-2.0.html Apache License 2.0 - * @ORM\Entity - * @ORM\Table(name="product") - */ -class Product -{ - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue(strategy="AUTO") - */ - protected $id; - - /** - * @ORM\Column(type="string", length=100) - */ - protected $name; - - /** - * @ORM\Column(type="decimal", scale=2) - */ - protected $price; - - /** - * @ORM\Column(type="text") - */ - protected $description; -} -``` - -アノテーションは `@` から始まる行で、わかりやすく強調表示されます。ただしPHPモードは[PSR-5: PHPDoc (草案)](https://github.com/phpDocumentor/fig-standards/blob/master/proposed/phpdoc.md)の完全なサポートは実装していません。我々は将来的にこれらの強調表示を厳密にサポートしたいと考えていますが、現在の実装は限定的です。詳しくは[#478](https://github.com/emacs-php/php-mode/issues/478)をご覧ください。 - -### コーディングスタイル - -PHPモードはデフォルトでは`php-enable-default-coding-style`関数で設定される合理的なインデントと整形スタイルを提供します。また、ほかの有用なコーディングスタイルも提供しているので、以下の関数を通じて設定することができます。 - -1. `php-enable-pear-coding-style` -2. `php-enable-drupal-coding-style` -3. `php-enable-wordpress-coding-style` -4. `php-enable-symfony2-coding-style` -5. `php-enable-psr2-coding-style` - -`M-x customize-group php`で‘PHP Mode Coding Style’を探してカスタマイズメニューを探して、デフォルトのコーディングスタイルの設定を有効化することができます。以下のように、コーディングスタイルごとにフックを有効化することもできます。 - -```lisp -(add-hook 'php-mode-hook 'php-enable-default-coding-style) -``` - -#### Symfony2 Style - -このスタイルではメソッドチェーンのインデントの継続とぶらさがったセミコロンを整形できます。 - - -```php - $user1 - ->setCreateDate(new \DateTime('2007-05-07 01:34:45')) - ->setLastDate(new \DateTime('2012-08-18 19:03:02')) - ->setUsername('jay') - ; -``` - -このスタイルはSymfony2のコードベースで広く利用されていますが、慣習についての文書で明示的に言及されているものではありません。 - -### HTMLテンプレートのサポートを無効化する - -多くの開発者はPHPモードで純粋なPHPスクリプト(HTMLテンプレートを含まないもの)を編集します。HTMLとの互換レイヤーはPHPモードの歴史的な機能ですが、完全には機能していません。速度の低下や強調表示を破壊するおそれがあるなどの副作用があります。変数`php-template-compatibility`を`nil`にセットすると、HTMLとの互換性を無効化することができます。HTMLやその他のマークアップ言語のテンプレートエンジンを含むPHPスクリプトを開発する際は[Web Mode][]は優れた選択肢です。 - -### Subword Mode - -GNU Emacsには[Subword Mode][]という機能があり、このマイナーモードは[キャメルケース][camelCase]の部分を別の単語のように移動することができます。たとえば、PHPモードはデフォルトでは変数`$fooBarBaz`を一つの単語として扱います。しかしSubword Modeを有効にすればEmacsはこの変数名を3つの単語として扱い、単語関係のコマンド(`M-f`, `M-b`, `M-d`など)はカーソル位置のキャメルケースの各部分に影響します。 - -もしPHPファイルで常にSubword Modeを有効化したいならば、以下のように設定できます。 - -```lisp -(add-hook 'php-mode-hook (lambda () (subword-mode 1))) -``` - -キーバインド `C-c C-w` はSubword Modeのオンとオフを切り替えます。 - -### 現在のclass/namespaceを挿入する - -```el -(with-eval-after-load 'php-mode - (define-key php-mode-map (kbd "C-c C--") 'php-current-class) - (define-key php-mode-map (kbd "C-c C-=") 'php-current-namespace)) -``` - -PHPプログラミングのためのパッケージ ----------------------------------- - -- 入力補完 - - [ac-php](https://github.com/xcwen/ac-php): [company-mode](https://github.com/company-mode/company-mode) and [auto-complete](https://github.com/auto-complete/auto-complete) for PHP -- 構文チェック - - [flycheck](https://github.com/flycheck/flycheck/): On the fly syntax checker - - [flymake-php](https://github.com/purcell/flymake-php): flymake for PHP files -- スニペット - - [php-auto-yasnippets](https://github.com/ejmr/php-auto-yasnippets): Dynamically Generated YASnippets for PHP Code -- ドキュメント - - [ggtags](https://github.com/leoliu/ggtags): eldoc by using GNU global tags - - [php-eldoc](https://github.com/sabof/php-eldoc): eldoc backend for PHP -- テスト - - [phpunit](https://github.com/nlamirault/phpunit.el): phpunit test command tool -- コーディングスタイル - - [phpcbf](https://github.com/nishimaki10/emacs-phpcbf): PHP_CodeSniffer for Emacs -- Semantic - - [ede-php-autoload](https://github.com/stevenremot/ede-php-autoload): Semantic for PHP -- フレームワーク - - [cake](https://github.com/k1LoW/emacs-cake): minor-mode for CakePHP - - [cake2](https://github.com/k1LoW/emacs-cake2): minor-mode for CakePHP2 +## 不具合を報告する +バグ報告の際には `M-x php-mode-debug` の出力を含めてください。この情報は問題の再現に役立ちます。 貢献するには ----------------- [CONTRIBUTING.md](CONTRIBUTING.md#japanese)をご覧ください。 -Wiki --------- - -GitHubのプロジェクトページには[wiki][]があり、自由に編集して構いません。このWikiには今後追加する計画のある機能やバグが掲載されています。また、PHPモードをより使いやすくするためのTipsを追加できます。 - ## 著作権 PHP Modeは[GNU General Public License Version 3][gpl-v3] (GPLv3) でライセンスされています。 @@ -310,7 +93,7 @@ PHP Modeは[GNU General Public License Version 3][gpl-v3] (GPLv3) でライセ このプロジェクトは2017年まで[Eric James Michael Ritz][@ejmr]によりメンテナンスされていました。現在は[Friends of Emacs-PHP Development][@emacs-php]コミュニティが引き継いで開発しています。 > ``` -> Copyright (C) 2022 Friends of Emacs-PHP development +> Copyright (C) 2023 Friends of Emacs-PHP development > Copyright (C) 1999, 2000, 2001, 2003, 2004 Turadg Aleahmad > 2008 Aaron S. Hawley > 2011, 2012, 2013, 2014, 2015, 2016, 2017 Eric James Michael Ritz @@ -334,31 +117,14 @@ PHP Modeは[GNU General Public License Version 3][gpl-v3] (GPLv3) でライセ [@haxney]: https://github.com/haxney [@turadg]: https://github.com/turadg [Authors]: https://github.com/emacs-php/php-mode/wiki/Authors -[Cask]: https://github.com/cask/cask [Contributors]: https://github.com/emacs-php/php-mode/graphs/contributors -[MELPA Stable]: https://stable.melpa.org/ -[MELPA]: https://melpa.org/ -[Subword Mode]: https://www.gnu.org/software/emacs/manual/html_node/ccmode/Subword-Movement.html [Supported Version]: https://github.com/emacs-php/php-mode/wiki/Supported-Version -[Web Mode]: http://web-mode.org/ -[camelCase]: https://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%A3%E3%83%A1%E3%83%AB%E3%82%B1%E3%83%BC%E3%82%B9 -[cc mode]: https://www.gnu.org/software/emacs/manual/html_mono/ccmode.html -[elpa-php-mode]: https://packages.debian.org/sid/elpa-php-mode -[gpl-v3]: https://www.gnu.org/licenses/quick-guide-gplv3.html -[issue-430]: https://github.com/emacs-php/php-mode/issues/430 -[nongnu-devel-elpa-badge]: https://elpa.nongnu.org/nongnu-devel/php-mode.svg -[nongnu-devel-elpa]: https://elpa.nongnu.org/nongnu-devel/php-mode.html +[gpl-v3]: https://www.gnu.org/licenses/gpl-3.0 [nongnu-elpa-badge]: https://elpa.nongnu.org/nongnu/php-mode.svg [nongnu-elpa]: https://elpa.nongnu.org/nongnu/php-mode.html [melpa-badge]: http://melpa.org/packages/php-mode-badge.svg +[melpa-getting-started]: https://melpa.org/#/getting-started [melpa-link]: http://melpa.org/#/php-mode -[melpa-stable-badge]: http://stable.melpa.org/packages/php-mode-badge.svg -[melpa-stable-link]: http://stable.melpa.org/#/php-mode -[package]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Packages.html -[php-elisp-stretch]: https://packages.debian.org/stretch/php-elisp -[php-elisp-ubuntu1810]: https://packages.ubuntu.com/cosmic/php-elisp -[php-mode-packages]: https://repology.org/project/emacs:php-mode/versions [php-mode]: https://github.com/emacs-php/php-mode -[php-suite]: https://github.com/emacs-php/php-suite [wiki]: https://github.com/emacs-php/php-mode/wiki -[wiki-manual-installation-ja]: https://github.com/emacs-php/php-mode/wiki/Manual-installation-ja +[wiki-manual-installation]: https://github.com/emacs-php/php-mode/wiki/Manual-installation-ja diff --git a/README.md b/README.md index 54248ed4..490b3f08 100644 --- a/README.md +++ b/README.md @@ -1,285 +1,90 @@

Emacs PHP Mode

-[![Emacs: 28.1](https://img.shields.io/badge/Emacs-28.1-blue.svg)](https://www.gnu.org/software/emacs/) -[![lang: PHP 8.1](https://img.shields.io/badge/lang-PHP%208.1-brightgreen.svg)](https://php.net/manual/migration81.php) -[![lang: PHP 7](https://img.shields.io/badge/lang-PHP%207-green.svg)](https://php.net/downloads.php) +[![Emacs: 30.0](https://img.shields.io/badge/Emacs-30.0-blue.svg)](https://www.gnu.org/software/emacs/) +[![lang: PHP 8.4](https://img.shields.io/badge/lang-PHP%208.4-brightgreen.svg)](https://www.php.net/releases/8.4/) [![Build Status](https://github.com/emacs-php/php-mode/workflows/CI/badge.svg)](https://github.com/emacs-php/php-mode/actions) -[![GPL v3](https://img.shields.io/badge/license-GPL_v3-green.svg)](http://www.gnu.org/licenses/gpl-3.0.txt)
+[![GPL v3](https://img.shields.io/badge/license-GPL_v3-green.svg)][gpl-v3]
[![NonGNU ELPA][nongnu-elpa-badge]][nongnu-elpa] [![melpa badge][melpa-badge]][melpa-link] A powerful and flexible Emacs major mode for editing PHP scripts +English   |   [日本語](README.ja.md) +
Please submit any bug reports or feature requests by creating issues on [the GitHub page for PHP Mode][php-mode]. -Installation ------------- - -**PHP Mode works on Emacs 25.2 or later.** PHP Mode may work with older versions of Emacs but this is not guaranteed. Bug reports for problems related to using PHP Mode with older versions of Emacs will most like *not* be addressed. - -The current support policy can be found on the [Supported Version] page. - -### **(RECOMMENDED)** Install from NonGNU ELPA - -[![NonGNU ELPA][nongnu-elpa-badge]][nongnu-elpa] [![NonGNU-devel ELPA][nongnu-devel-elpa-badge]][nongnu-devel-elpa] - -Emacs 28 (latest stable release) includes [NonGNU ELPA](https://elpa.nongnu.org/) as the default package repository. - -### Install from MELPA - -[![melpa badge][melpa-badge]][melpa-link] [![melpa stable badge][melpa-stable-badge]][melpa-stable-link] - -With GNU Emacs 24 or later then you can use its [package][] feature (or [Cask][]) to install PHP Mode from [MELPA][] or [MELPA Stable][]. - -### Install using OS package system - -PHP Mode is available from [package systems provided by several OSs][php-mode-packages]. On Debian, and operating systems derived from it, such as Ubuntu, the easiest way to add PHP support to Emacs is `sudo apt install elpa-php-mode`. "Stable" releases of these operating systems include a well-tested frozen version of this package that is always older than the latest upstream version of PHP Mode. Autoloads and byte-compilation are automatic. - -Please consider installing the package provided in `sid` (aka: "unstable", a rolling release) in order to benefit from the latest PHP Mode features and performance improvements. The latest version can be found here: [`elpa-php-mode`][elpa-php-mode]. Investigate "apt-pinning" to make tracking this update stream automatic. - -Also, the `php-elisp` package provided by [Debian 9 (stretch)][php-elisp-stretch] and [Ubuntu 18.10][php-elisp-ubuntu1810] and earlier is [extremely old][issue-430], so **PLEASE DO NOT INSTALL IT**. - -### Manual installation - -If you don't want to depend on a package manager, you can install Lisp files directly in the traditional way. See [Manual installation][wiki-manual-installation] for our recommended setup method. - -### Configuration - -PHP Mode's default style might not be what you expect. Before filing a bug, please try the following: `C-h v php-mode-coding-style`, to get the list of available styles. Then use `M-x php-set-style` for each of these, to check to see if one of them is to your liking. Use `TODO: Please comment on what config method you'd like to recommend to users` - -Reporting Bugs --------------- - -When reporting a bug please run the function `M-x php-mode-debug` and include its output in your bug report. This helps up reproduce any problem you may have. - - -Experimental and In-Progress Features -------------------------------------- - -### CC Mode, CEDET, EDE, and Semantic - -In 2013 Daniel Haxney began rewriting parts of PHP Mode in terms of Emacs' built-in CC Mode. This laid the foundation for incorporating some of the inherit IDE-ish features of Emacs, such as CEDET, EDE, and Semantic. Support for these tools continues to improve thanks to the work of Andrea Turso, Steven Rémot, Joris Steyn, and others. If you wish to test, contribute to, or simply experiment with such features then [this thread is a good place to start](https://github.com/emacs-php/php-mode/issues/256). - -### PHP 7 Support - -PHP 7 has been released. PHP Mode supports the following features and changes from PHP 7: - -1. Type-hints for return values in functions and methods receive syntax highlighting in the same way as type-hints for function and method parameters. - -2. PHP Mode treats `yield from` as keyword in the same way it already does for a sole `yield`. - -3. It recognizes `strict_types` as a special declaration in the same way as `ticks`. - - -Features --------- - -### New Keywords - -Now PHP Mode supports syntax highlighting for new keywords which PHP 5.4 introduced, e.g. those related to traits, such as `insteadof`. Also supported are the older keywords `clone` and `default`. - -### Constants - -Syntax highlighting includes every magic constant and predefined constant listed on the official PHP site. However, some constants from specific extensions are not currently included. - -### Traits, Interfaces, and Namespaces - -Traits, interfaces, and namespaces now appear under Imenu listings. Fontification behaves properly for namespaces as well, so that code like `namespace Foo\Bar\Baz` no longer looks like a warning. This is also true for namespace aliases, e.g. `use as `; currently the aliased name is not listed in Imenu, but future versions will address this. - -### Treatment of Underscores - -PHP Mode treats underscores as ‘symbol constituents’ (in Emacs terminology) so that you can use keys like `M-f` and `M-b` to move through the individual parts of a variable name like `$foo_bar_baz`. - -### Chained Method Calls +> [!NOTE] +> The [latest version][releases] of PHP Mode supports Emacs 30. +> Please feel free to [open a discussion][discussions-emacs30] if you have any issues upgrading to Emacs 30. -PHP Mode can align method calls over multiple lines anchored around the `->` operator, e.g.: - -```php -$object->foo() - ->bar() - ->baz(); -``` - -This behaviour is off by default, but you can customize the variable `php-mode-lineup-cascaded-calls` to enable this. - -**Note:** Alignment will only work if you use one of the php-mode coding styles or inherit one of the styles. - -### Nested Array Formatting - -Nested function calls and `array()` structures now look better by default (or at least in my opinion). Here is an example of the style: +> [!WARNING] +> After upgrading Emacs, when you open a PHP file for the first time, you may encounter errors related to CC Mode. These errors occur because a previously byte-compiled version of PHP Mode, cached on your disk, differs from the newly installed one. Reinstalling PHP Mode should resolve the issue. +> +> Try running **`M-x php-mode-debug-reinstall`** or **`M-x package-reinstall php-mode`**. -```php -$results = Post::model()->find( - array( - 'select' => 'title', - 'condition' => 'postID=:postID', - 'params' => array(':postID' => 10), - ) -); -``` +[releases]: https://github.com/emacs-php/php-mode/releases +[discussions-emacs30]: https://github.com/emacs-php/php-mode/discussions/798 -### Anonymous Functions +## Installation -Anonymous functions such as +**PHP Mode works with Emacs 27.1 or later.** For details on supported versions, see [Supported Version]. +On Emacs 28 or later, you can install it simply by running: -```php -$greet = function($name) { ... }; ``` - -will now appear on Imenu; in this case the name will be `$greet`. - -### Flymake Support - -By customizing the variable `php-executable` you can enable Flymake mode in order to see warnings and errors in real-time as you write code. - -### Search Local Documentation - -The key command `C-c C-f` will search the PHP website for documentation on the word under the cursor. However, if you have a [local copy of the PHP documentation](http://us2.php.net/download-docs.php) then PHP Mode will try searching that documentation first. All you need to do is customize the variable `php-manual-path` and give it the path to your copy of the documentation. If PHP Mode cannot find something locally then it will still fallback on searching the PHP website. - -### Executing Regions of PHP - -The command `php-send-region`, which is bound to `C-c C-r` by default, will execute the selected region of PHP code. In conjunction with the Emacs command `C-x h` you can use this to execute an entire file. Any output will appear in a buffer called `*PHP*`. - -### PHPDoc Tag / Annotation Highlighting - -PHPDoc is a documentation format similar to [JavaDoc](https://en.wikipedia.org/wiki/Javadoc). - -There are `@param`, `@return`, `@var`... etc in the notation called **tag**, look at [list of tags defined by phpDocumentor2](https://phpdoc.org/docs/latest/references/phpdoc/tags/index.html). (These tags are compatible with static type checkers like PhpStorm and [Phan](https://github.com/etsy/phan).) - -In addition, it also partially supports notation called **annotation**. Annotation has a slightly different grammar from tag, and the example is `@Annotation(attr1="vvv", attr2="zzz")`. - -[Symfony](https://symfony.com/) project and [Go! AOP](https://github.com/goaop/framework) and some projects/frameworks use annotation grammer based on [Doctrine Annotations](https://www.doctrine-project.org/projects/doctrine-annotations/en/latest/index.html). - -```php -/** - * Summary of Product class - * - * @copyright 2112 John Doe - * @license https://spdx.org/licenses/Apache-2.0.html Apache License 2.0 - * @ORM\Entity - * @ORM\Table(name="product") - */ -class Product -{ - /** - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue(strategy="AUTO") - */ - protected $id; - - /** - * @ORM\Column(type="string", length=100) - */ - protected $name; - - /** - * @ORM\Column(type="decimal", scale=2) - */ - protected $price; - - /** - * @ORM\Column(type="text") - */ - protected $description; -} +M-x package-install php-mode ``` -The annotations are the lines that begin with the `@` character, and PHP Mode will give these special highlighting to help them stand out. +By [adding MELPA to `package-archives`][melpa-getting-started], you can extend Emacs with numerous packages from the web. -PHP Mode has not fully supported [PSR-5: PHPDoc (Draft)](https://github.com/phpDocumentor/fig-standards/blob/master/proposed/phpdoc.md) yet. We want to support them, but the current implementation still limited. See issue [#478](https://github.com/emacs-php/php-mode/issues/478) for details. +If you prefer not to rely on a package manager, you can install the Lisp files directly in the traditional manner. See [Manual installation][wiki-manual-installation] for our recommended method. -### Coding Styles +## Configuration -By default PHP Mode tries to provide a reasonable style for indentation and formatting, which you can use via the function `php-enable-default-coding-style`. However, it provides other options suited for particular projects which you may find useful. Other coding styles are available through these functions: +### Personal Settings -1. `php-enable-pear-coding-style` -2. `php-enable-drupal-coding-style` -3. `php-enable-wordpress-coding-style` -4. `php-enable-symfony2-coding-style` -5. `php-enable-psr2-coding-style` - -They will help format your code for PEAR/PSR-2 projects, or work on Drupal, WordPress, and Symfony2 software, respectively. You may enable any of them by default by running `M-x customize-group php` and looking for the ‘PHP Mode Coding Style’ option. You may also enable any of these via a hook, e.g. +You can add configurations for PHP Mode in your `.emacs` file (`~/.emacs.d/init.el`): ```lisp -(add-hook 'php-mode-hook 'php-enable-default-coding-style) -``` - -#### Symfony2 Style +(defun my-php-mode-init () + (subword-mode 1) + (setq-local show-trailing-whitespace t) + (setq-local ac-disable-faces '(font-lock-comment-face font-lock-string-face)) + (add-hook 'hack-local-variables-hook 'php-ide-turn-on nil t)) -With this style method call chains can be formatted with indented continuation and a hanging semi-colon: - -```php - $user1 - ->setCreateDate(new \DateTime('2007-05-07 01:34:45')) - ->setLastDate(new \DateTime('2012-08-18 19:03:02')) - ->setUsername('jay') - ; +(with-eval-after-load 'php-mode + (add-hook 'php-mode-hook #'my-php-mode-init) + (custom-set-variables + '(php-mode-coding-style 'psr2) + '(php-mode-template-compatibility nil) + '(php-imenu-generic-expression 'php-imenu-generic-expression-simple)) + + ;; If you find phpcs to be bothersome, you can disable it. + (when (require 'flycheck nil) + (add-to-list 'flycheck-disabled-checkers 'php-phpmd) + (add-to-list 'flycheck-disabled-checkers 'php-phpcs))) ``` -This style is used widely throughout Symfony2 source code even if it is not explicitly mentioned in their conventions documents. +### Project Setting -### Avoid HTML Template Compatibility - -Many developers use PHP Mode to edit pure PHP scripts (e.g. files with only PHP and no HTML). A basic compatibility layer with HTML has historically been part of PHP Mode but it does not work perfectly and can cause some bad side effects such as slowness and incorrect font locking. Configuring the `php-template-compatibility` property with a `nil` will cancel any attempt of HTML compatibility. [Web Mode](http://web-mode.org/) is a great alternative to PHP Mode if you need to work with PHP scripts that do contain HTML and other markup. - -### Subword Mode - -GNU Emacs comes with [Subword Mode][], a minor mode that allows you to navigate the parts of a [camelCase][] as if they were separate words. For example, PHP Mode treats the variable `$fooBarBaz` as a whole name by default. But if you enable Subword Mode then Emacs will treat the variable name as three separate words, and therefore word-related commands (e.g. `M-f`, `M-b`, `M-d`) will only affect the camelCase part of the name under the cursor. - -If you want to always use Subword Mode for PHP files then you can add this to your Emacs configuration: +You can add project-specific settings by creating a `.dir-locals.el` or `.dir-locals-2.el` file in the project's root directory. It is recommended not to put these files under version control, as they depend on the packages installed on each user's Emacs. ```lisp -(add-hook 'php-mode-hook (lambda () (subword-mode 1))) +((nil + (php-project-root . git) + (php-project-coding-style . psr2))) ``` -The key-binding `C-c C-w` will also toggle Subword Mode on and off. +## Reporting Bugs -### Insert current class/namespace +When reporting a bug, please run `M-x php-mode-debug` and include its output in your bug report. This helps us reproduce any issues you may be experiencing. -```el -(with-eval-after-load 'php-mode - (define-key php-mode-map (kbd "C-c C--") 'php-current-class) - (define-key php-mode-map (kbd "C-c C-=") 'php-current-namespace)) -``` - -Other Packages for PHP programming ----------------------------------- - -- Completions - - [ac-php](https://github.com/xcwen/ac-php): [company-mode](https://github.com/company-mode/company-mode) and [auto-complete](https://github.com/auto-complete/auto-complete) for PHP -- Syntax checking - - [flycheck](https://github.com/flycheck/flycheck/): On the fly syntax checker - - [flymake-php](https://github.com/purcell/flymake-php): flymake for PHP files -- Snippet - - [php-auto-yasnippets](https://github.com/ejmr/php-auto-yasnippets): Dynamically Generated YASnippets for PHP Code -- Documentation - - [ggtags](https://github.com/leoliu/ggtags): eldoc by using GNU global tags - - [php-eldoc](https://github.com/sabof/php-eldoc): eldoc backend for PHP -- Testing - - [phpunit](https://github.com/nlamirault/phpunit.el): phpunit test command tool -- Style - - [phpcbf](https://github.com/nishimaki10/emacs-phpcbf): PHP_CodeSniffer for Emacs -- Semantic - - [ede-php-autoload](https://github.com/stevenremot/ede-php-autoload): Semantic for PHP -- Framework - - [cake](https://github.com/k1LoW/emacs-cake): minor-mode for CakePHP - - [cake2](https://github.com/k1LoW/emacs-cake2): minor-mode for CakePHP2 - - -How to Contribute ------------------ +## How to Contribute Please see [CONTRIBUTING.md](CONTRIBUTING.md#english). -The Wiki --------- - -The GitHub project page has a [wiki][] that you should feel free to edit. The wiki lists the features and bugs that are on plan to include in upcoming versions of PHP Mode. It is also a place to add any tips to make the mode more useful. - ## Copyright PHP Mode is licensed under [GNU General Public License Version 3][gpl-v3] (GPLv3). @@ -313,31 +118,14 @@ This project was maintained by [Eric James Michael Ritz][@ejmr] until 2017. Curr [@haxney]: https://github.com/haxney [@turadg]: https://github.com/turadg [Authors]: https://github.com/emacs-php/php-mode/wiki/Authors -[Cask]: https://github.com/cask/cask [Contributors]: https://github.com/emacs-php/php-mode/graphs/contributors -[MELPA Stable]: https://stable.melpa.org/ -[MELPA]: https://melpa.org/ -[Subword Mode]: https://www.gnu.org/software/emacs/manual/html_node/ccmode/Subword-Movement.html [Supported Version]: https://github.com/emacs-php/php-mode/wiki/Supported-Version -[Web Mode]: http://web-mode.org/ -[camelCase]: https://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%A3%E3%83%A1%E3%83%AB%E3%82%B1%E3%83%BC%E3%82%B9 -[cc mode]: https://www.gnu.org/software/emacs/manual/html_mono/ccmode.html -[elpa-php-mode]: https://packages.debian.org/sid/elpa-php-mode -[gpl-v3]: https://www.gnu.org/licenses/quick-guide-gplv3.html -[issue-430]: https://github.com/emacs-php/php-mode/issues/430 -[nongnu-devel-elpa-badge]: https://elpa.nongnu.org/nongnu-devel/php-mode.svg -[nongnu-devel-elpa]: https://elpa.nongnu.org/nongnu-devel/php-mode.html +[gpl-v3]: https://www.gnu.org/licenses/gpl-3.0 [nongnu-elpa-badge]: https://elpa.nongnu.org/nongnu/php-mode.svg [nongnu-elpa]: https://elpa.nongnu.org/nongnu/php-mode.html [melpa-badge]: http://melpa.org/packages/php-mode-badge.svg +[melpa-getting-started]: https://melpa.org/#/getting-started [melpa-link]: http://melpa.org/#/php-mode -[melpa-stable-badge]: http://stable.melpa.org/packages/php-mode-badge.svg -[melpa-stable-link]: http://stable.melpa.org/#/php-mode -[package]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Packages.html -[php-elisp-stretch]: https://packages.debian.org/stretch/php-elisp -[php-elisp-ubuntu1810]: https://packages.ubuntu.com/cosmic/php-elisp -[php-mode-packages]: https://repology.org/project/emacs:php-mode/versions [php-mode]: https://github.com/emacs-php/php-mode -[php-suite]: https://github.com/emacs-php/php-suite [wiki]: https://github.com/emacs-php/php-mode/wiki [wiki-manual-installation]: https://github.com/emacs-php/php-mode/wiki/Manual-installation diff --git a/lisp/php-align.el b/lisp/php-align.el index d55fab7a..e9acc469 100644 --- a/lisp/php-align.el +++ b/lisp/php-align.el @@ -1,13 +1,13 @@ ;;; php-align.el --- Alignment configuration for PHP -*- lexical-binding: t; -*- ;; Copyright (C) 2011 tetsujin (Yusuke Segawa) -;; Copyright (C) 2022 Friends of Emacs-PHP development +;; Copyright (C) 2023 Friends of Emacs-PHP development ;; Author: tetsujin (Yusuke Segawa) ;; Maintainer: USAMI Kenta ;; Keywords: php languages convenience align ;; Homepage: https://github.com/emacs-php/php-mode -;; Version: 1.24.1 +;; Version: 1.26.1 ;; License: GPL-3.0-or-later ;; This program is free software; you can redistribute it and/or modify diff --git a/lisp/php-complete.el b/lisp/php-complete.el new file mode 100644 index 00000000..f2b1aa42 --- /dev/null +++ b/lisp/php-complete.el @@ -0,0 +1,124 @@ +;;; php-complete.el --- PHP auto-compiletion functions -*- lexical-binding: t; -*- + +;; Copyright (C) 2023 Friends of Emacs-PHP development +;; Copyright (C) 2021, 2022 Free Software Foundation, Inc. + +;; Author: USAMI Kenta + +;; Created: 18 Sep 2022 +;; Version: 1.26.1 +;; Keywords: languages, php + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; Provide auto-compiletion functions. + +;; These functions are copied function from GNU ELPA. +;; +;; - cape--table-with-properties (cape.el) +;; - cape--bounds (cape.el) +;; - cape--interactive (cape.el) + +;;; Code: +(eval-when-compile + (require 'cl-lib)) +(require 'php) +(require 'php-defs) + +;;;###autoload +(defgroup php-complete nil + "Auto completion for PHP edition." + :tag "PHP Completion" + :group 'php-mode) + +;;;###autoload +(defcustom php-complete-function-modules '(bcmath core gmp libxml intl mbstring pcntl posix sodium xml xmlwriter) + "Module names for function names completion." + :tag "PHP Complete Function Modules" + :type (eval-when-compile `(set ,@(mapcar (lambda (elm) (list 'const (car elm))) + php-defs-functions-alist))) + :safe (lambda (value) (and (listp value) (cl-loop for v in values + always (assq v php-defs-functions-alist)))) + :group 'php-complete) + +;;; Cape functions: + +;; These functions are copied from cape.el package. https://github.com/minad/cape +;; Thanks to original author Daniel Mendler (@minad) + +(cl-defun php-complete--cape-table-with-properties (table &key category (sort t) &allow-other-keys) + "Create completion TABLE with properties. +CATEGORY is the optional completion category. +SORT should be nil to disable sorting." + (if (or (not table) (and (not category) sort)) + table + (let ((metadata `(metadata + ,@(and category `((category . ,category))) + ,@(and (not sort) '((display-sort-function . identity) + (cycle-sort-function . identity)))))) + (lambda (str pred action) + (if (eq action 'metadata) + metadata + (complete-with-action action table str pred)))))) + +(defun php-complete--cape-bounds (thing) + "Return bounds of THING." + (or (bounds-of-thing-at-point thing) (cons (point) (point)))) + +(defun php-complete--cape-interactive (capf) + "Complete with CAPF." + (let ((completion-at-point-functions (list capf))) + (or (completion-at-point) (user-error "%s: No completions" capf)))) + +;;; Variables: +(defvar php-complete--functions-cache (make-hash-table :test #'equal)) + +;;; Data source functions: +(defun php-complete--functions () + "Return PHP function names." + (let* ((modules (sort php-complete-function-modules #'string<)) + (functions (gethash modules php-complete--functions-cache))) + (unless functions + (setq functions (sort (cl-loop for module in modules + append (assq module php-defs-functions-alist)) + #'string<)) + (puthash modules functions php-complete--functions-cache)) + functions)) + +;;; Compiletion function: + +;;;###autoload +(defun php-complete-complete-function (&optional interactive) + "Complete PHP keyword at point. + +If INTERACTIVE is nil the function acts like a capf." + (interactive (list t)) + (if interactive + (php-complete--cape-interactive #'php-complete-complete-function) + (let ((bounds (php-complete--cape-bounds 'symbol)) + (tokens (nreverse (php-leading-tokens 2)))) + `(,(car bounds) ,(cdr bounds) + ,(php-complete--cape-table-with-properties + (unless (or (member (nth 0 tokens) '("->" "::")) + (string-prefix-p "$" (nth 1 tokens))) + (php-complete--functions)) + :category 'cape-keyword) + :annotation-function (lambda (_) " PHP functions") + :company-kind (lambda (_) 'keyword) + :exclusive 'no)))) + +(provide 'php-complete) +;;; php-complete.el ends here diff --git a/lisp/php-defs.el b/lisp/php-defs.el new file mode 100644 index 00000000..8be83138 --- /dev/null +++ b/lisp/php-defs.el @@ -0,0 +1,3892 @@ +;;; php-defs.el --- Provide PHP keyword definitions -*- lexical-binding: t; -*- + +;; Copyright (C) 2023 Friends of Emacs-PHP development + +;; Author: USAMI Kenta +;; Created: 5 Mar 2022 +;; Version: 1.26.1 +;; Keywords: languages, php + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; This file provides common functions for PHP codes. + +;;; Code: + +(defvar php-defs-functions-alist + '((apache + "apache_child_terminate" + "apache_get_modules" + "apache_get_version" + "apache_getenv" + "apache_lookup_uri" + "apache_note" + "apache_request_headers" + "apache_response_headers" + "apache_setenv" + "virtual") + (apcu + "apcu_add" + "apcu_cache_info" + "apcu_cas" + "apcu_clear_cache" + "apcu_dec" + "apcu_delete" + "apcu_enabled" + "apcu_entry" + "apcu_exists" + "apcu_fetch" + "apcu_inc" + "apcu_key_info" + "apcu_sma_info" + "apcu_store") + (bcmath + "bcadd" + "bccomp" + "bcdiv" + "bcmod" + "bcmul" + "bcpow" + "bcpowmod" + "bcscale" + "bcsqrt" + "bcsub") + (bzip2 + "bzclose" + "bzcompress" + "bzdecompress" + "bzerrno" + "bzerror" + "bzerrstr" + "bzflush" + "bzopen" + "bzread" + "bzwrite") + (calendar + "cal_days_in_month" + "cal_from_jd" + "cal_info" + "cal_to_jd" + "easter_date" + "easter_days" + "frenchtojd" + "gregoriantojd" + "jddayofweek" + "jdmonthname" + "jdtofrench" + "jdtogregorian" + "jdtojewish" + "jdtojulian" + "jdtounix" + "jewishtojd" + "unixtojd") + (com_dotnet + "com_create_guid" + "com_event_sink" + "com_get_active_object" + "com_load_typelib" + "com_message_pump" + "com_print_typeinfo" + "variant_abs" + "variant_add" + "variant_and" + "variant_cast" + "variant_cat" + "variant_cmp" + "variant_date_from_timestamp" + "variant_date_to_timestamp" + "variant_div" + "variant_eqv" + "variant_fix" + "variant_get_type" + "variant_idiv" + "variant_imp" + "variant_int" + "variant_mod" + "variant_mul" + "variant_neg" + "variant_not" + "variant_or" + "variant_pow" + "variant_round" + "variant_set" + "variant_set_type" + "variant_sub" + "variant_xor") + (commonmark + "CommonMark\\Parse" + "CommonMark\\Render" + "CommonMark\\Render\\HTML" + "CommonMark\\Render\\Latex" + "CommonMark\\Render\\Man" + "CommonMark\\Render\\XML") + (componere + "Componere\\cast" + "Componere\\cast_by_ref") + (core + "abs" + "acos" + "acosh" + "addcslashes" + "addslashes" + "array_change_key_case" + "array_chunk" + "array_column" + "array_combine" + "array_count_values" + "array_diff" + "array_diff_assoc" + "array_diff_key" + "array_diff_uassoc" + "array_diff_ukey" + "array_fill" + "array_fill_keys" + "array_filter" + "array_flip" + "array_intersect" + "array_intersect_assoc" + "array_intersect_key" + "array_intersect_uassoc" + "array_intersect_ukey" + "array_is_list" + "array_key_exists" + "array_key_first" + "array_key_last" + "array_keys" + "array_map" + "array_merge" + "array_merge_recursive" + "array_multisort" + "array_pad" + "array_pop" + "array_product" + "array_push" + "array_rand" + "array_reduce" + "array_replace" + "array_replace_recursive" + "array_reverse" + "array_search" + "array_shift" + "array_slice" + "array_splice" + "array_sum" + "array_udiff" + "array_udiff_assoc" + "array_udiff_uassoc" + "array_uintersect" + "array_uintersect_assoc" + "array_uintersect_uassoc" + "array_unique" + "array_unshift" + "array_values" + "array_walk" + "array_walk_recursive" + "arsort" + "asin" + "asinh" + "asort" + "assert_options" + "atan" + "atan2" + "atanh" + "base64_decode" + "base64_encode" + "base_convert" + "basename" + "bin2hex" + "bindec" + "boolval" + "call_user_func" + "call_user_func_array" + "ceil" + "chdir" + "checkdate" + "checkdnsrr" + "chgrp" + "chmod" + "chop" + "chown" + "chr" + "chroot" + "chunk_split" + "class_alias" + "class_exists" + "class_implements" + "class_parents" + "class_uses" + "clearstatcache" + "cli_get_process_title" + "cli_set_process_title" + "closedir" + "closelog" + "compact" + "connection_aborted" + "connection_status" + "constant" + "convert_cyr_string" + "convert_uudecode" + "convert_uuencode" + "copy" + "cos" + "cosh" + "count" + "count_chars" + "crc32" + "create_function" + "crypt" + "ctype_alnum" + "ctype_alpha" + "ctype_cntrl" + "ctype_digit" + "ctype_graph" + "ctype_lower" + "ctype_print" + "ctype_punct" + "ctype_space" + "ctype_upper" + "ctype_xdigit" + "current" + "date" + "date_add" + "date_create" + "date_create_from_format" + "date_create_immutable" + "date_create_immutable_from_format" + "date_date_set" + "date_default_timezone_get" + "date_default_timezone_set" + "date_diff" + "date_format" + "date_get_last_errors" + "date_interval_create_from_date_string" + "date_interval_format" + "date_isodate_set" + "date_modify" + "date_offset_get" + "date_parse" + "date_parse_from_format" + "date_sub" + "date_sun_info" + "date_sunrise" + "date_sunset" + "date_time_set" + "date_timestamp_get" + "date_timestamp_set" + "date_timezone_get" + "date_timezone_set" + "debug_backtrace" + "debug_print_backtrace" + "debug_zval_dump" + "decbin" + "dechex" + "decoct" + "define" + "defined" + "deg2rad" + "delete" + "die" + "dir" + "dirname" + "disk_free_space" + "disk_total_space" + "diskfreespace" + "dl" + "dns_check_record" + "dns_get_mx" + "dns_get_record" + "doubleval" + "each" + "enum_exists" + "error_clear_last" + "error_get_last" + "error_log" + "error_reporting" + "escapeshellarg" + "escapeshellcmd" + "eval" + "exec" + "exit" + "exp" + "explode" + "expm1" + "extension_loaded" + "extract" + "fastcgi_finish_request" + "fclose" + "fdatasync" + "fdiv" + "feof" + "fflush" + "fgetc" + "fgetcsv" + "fgets" + "fgetss" + "file" + "file_exists" + "file_get_contents" + "file_put_contents" + "fileatime" + "filectime" + "filegroup" + "fileinode" + "filemtime" + "fileowner" + "fileperms" + "filesize" + "filetype" + "filter_has_var" + "filter_id" + "filter_input" + "filter_input_array" + "filter_list" + "filter_var" + "filter_var_array" + "finfo_buffer" + "finfo_close" + "finfo_file" + "finfo_open" + "finfo_set_flags" + "floatval" + "flock" + "floor" + "flush" + "fmod" + "fnmatch" + "fopen" + "forward_static_call" + "forward_static_call_array" + "fpassthru" + "fprintf" + "fputcsv" + "fputs" + "fread" + "fscanf" + "fseek" + "fsockopen" + "fstat" + "fsync" + "ftell" + "ftruncate" + "func_get_arg" + "func_get_args" + "func_num_args" + "function_exists" + "fwrite" + "gc_collect_cycles" + "gc_disable" + "gc_enable" + "gc_enabled" + "gc_mem_caches" + "gc_status" + "get_browser" + "get_called_class" + "get_cfg_var" + "get_class" + "get_class_methods" + "get_class_vars" + "get_current_user" + "get_debug_type" + "get_declared_classes" + "get_declared_interfaces" + "get_declared_traits" + "get_defined_constants" + "get_defined_functions" + "get_defined_vars" + "get_extension_funcs" + "get_headers" + "get_html_translation_table" + "get_include_path" + "get_included_files" + "get_loaded_extensions" + "get_magic_quotes_gpc" + "get_magic_quotes_runtime" + "get_mangled_object_vars" + "get_meta_tags" + "get_object_vars" + "get_parent_class" + "get_required_files" + "get_resource_id" + "get_resource_type" + "get_resources" + "getallheaders" + "getcwd" + "getdate" + "getenv" + "gethostbyaddr" + "gethostbyname" + "gethostbynamel" + "gethostname" + "getlastmod" + "getmxrr" + "getmygid" + "getmyinode" + "getmypid" + "getmyuid" + "getopt" + "getprotobyname" + "getprotobynumber" + "getrandmax" + "getrusage" + "getservbyname" + "getservbyport" + "gettimeofday" + "gettype" + "glob" + "gmdate" + "gmmktime" + "gmstrftime" + "hash" + "hash_algos" + "hash_copy" + "hash_equals" + "hash_file" + "hash_final" + "hash_hkdf" + "hash_hmac" + "hash_hmac_algos" + "hash_hmac_file" + "hash_init" + "hash_pbkdf2" + "hash_update" + "hash_update_file" + "hash_update_stream" + "header" + "header_register_callback" + "header_remove" + "headers_list" + "headers_sent" + "hebrev" + "hebrevc" + "hex2bin" + "hexdec" + "highlight_file" + "highlight_string" + "hrtime" + "html_entity_decode" + "htmlentities" + "htmlspecialchars" + "htmlspecialchars_decode" + "http_build_query" + "http_response_code" + "hypot" + "idate" + "ignore_user_abort" + "implode" + "in_array" + "inet_ntop" + "inet_pton" + "ini_alter" + "ini_get" + "ini_get_all" + "ini_restore" + "ini_set" + "intdiv" + "interface_exists" + "intval" + "ip2long" + "is_a" + "is_array" + "is_bool" + "is_callable" + "is_countable" + "is_dir" + "is_double" + "is_executable" + "is_file" + "is_finite" + "is_float" + "is_infinite" + "is_int" + "is_integer" + "is_iterable" + "is_link" + "is_long" + "is_nan" + "is_null" + "is_numeric" + "is_object" + "is_readable" + "is_resource" + "is_scalar" + "is_string" + "is_subclass_of" + "is_uploaded_file" + "is_writable" + "is_writeable" + "iterator_apply" + "iterator_count" + "iterator_to_array" + "join" + "json_decode" + "json_encode" + "json_last_error" + "json_last_error_msg" + "juliantojd" + "key" + "key_exists" + "krsort" + "ksort" + "lcfirst" + "lcg_value" + "lchgrp" + "lchown" + "levenshtein" + "link" + "linkinfo" + "list" + "localeconv" + "localtime" + "log" + "log10" + "log1p" + "long2ip" + "lstat" + "ltrim" + "mail" + "max" + "md5" + "md5_file" + "memory_get_peak_usage" + "memory_get_usage" + "metaphone" + "method_exists" + "microtime" + "mime_content_type" + "min" + "mkdir" + "mktime" + "money_format" + "move_uploaded_file" + "mt_getrandmax" + "mt_rand" + "mt_srand" + "natcasesort" + "natsort" + "net_get_interfaces" + "next" + "nl2br" + "nl_langinfo" + "number_format" + "ob_clean" + "ob_end_clean" + "ob_end_flush" + "ob_flush" + "ob_get_clean" + "ob_get_contents" + "ob_get_flush" + "ob_get_length" + "ob_get_level" + "ob_get_status" + "ob_gzhandler" + "ob_iconv_handler" + "ob_implicit_flush" + "ob_list_handlers" + "ob_start" + "ob_tidyhandler" + "octdec" + "opcache_compile_file" + "opcache_get_configuration" + "opcache_get_status" + "opcache_invalidate" + "opcache_is_script_cached" + "opcache_reset" + "opendir" + "openlog" + "ord" + "output_add_rewrite_var" + "output_reset_rewrite_vars" + "pack" + "parse_ini_file" + "parse_ini_string" + "parse_str" + "parse_url" + "passthru" + "password_algos" + "password_get_info" + "password_hash" + "password_needs_rehash" + "password_verify" + "pathinfo" + "pclose" + "pdo_drivers" + "pfsockopen" + "php_ini_loaded_file" + "php_ini_scanned_files" + "php_sapi_name" + "php_strip_whitespace" + "php_uname" + "phpcredits" + "phpdbg_break_file" + "phpdbg_break_function" + "phpdbg_break_method" + "phpdbg_break_next" + "phpdbg_clear" + "phpdbg_color" + "phpdbg_end_oplog" + "phpdbg_exec" + "phpdbg_get_executable" + "phpdbg_prompt" + "phpdbg_start_oplog" + "phpinfo" + "phpversion" + "pi" + "popen" + "pos" + "pow" + "preg_filter" + "preg_grep" + "preg_last_error" + "preg_last_error_msg" + "preg_match" + "preg_match_all" + "preg_quote" + "preg_replace" + "preg_replace_callback" + "preg_replace_callback_array" + "preg_split" + "prev" + "print_r" + "printf" + "proc_close" + "proc_get_status" + "proc_nice" + "proc_open" + "proc_terminate" + "property_exists" + "putenv" + "quoted_printable_decode" + "quoted_printable_encode" + "quotemeta" + "rad2deg" + "rand" + "random_bytes" + "random_int" + "range" + "rawurldecode" + "rawurlencode" + "readdir" + "readfile" + "readlink" + "realpath" + "realpath_cache_get" + "realpath_cache_size" + "register_shutdown_function" + "register_tick_function" + "rename" + "reset" + "restore_error_handler" + "restore_exception_handler" + "restore_include_path" + "rewind" + "rewinddir" + "rmdir" + "round" + "rsort" + "rtrim" + "sapi_windows_cp_conv" + "sapi_windows_cp_get" + "sapi_windows_cp_is_utf8" + "sapi_windows_cp_set" + "sapi_windows_generate_ctrl_event" + "sapi_windows_set_ctrl_handler" + "sapi_windows_vt100_support" + "scandir" + "seaslog_get_author" + "seaslog_get_version" + "serialize" + "session_abort" + "session_cache_expire" + "session_cache_limiter" + "session_commit" + "session_create_id" + "session_decode" + "session_destroy" + "session_encode" + "session_gc" + "session_get_cookie_params" + "session_id" + "session_module_name" + "session_name" + "session_regenerate_id" + "session_register_shutdown" + "session_reset" + "session_save_path" + "session_set_cookie_params" + "session_set_save_handler" + "session_start" + "session_status" + "session_unset" + "session_write_close" + "set_error_handler" + "set_exception_handler" + "set_file_buffer" + "set_include_path" + "set_time_limit" + "setcookie" + "setlocale" + "setrawcookie" + "settype" + "sha1" + "sha1_file" + "shell_exec" + "show_source" + "shuffle" + "similar_text" + "sin" + "sinh" + "sizeof" + "sleep" + "sort" + "soundex" + "spl_autoload" + "spl_autoload_call" + "spl_autoload_extensions" + "spl_autoload_functions" + "spl_autoload_register" + "spl_autoload_unregister" + "spl_classes" + "spl_object_hash" + "spl_object_id" + "sprintf" + "sqrt" + "srand" + "sscanf" + "stat" + "str_contains" + "str_ends_with" + "str_getcsv" + "str_ireplace" + "str_pad" + "str_repeat" + "str_replace" + "str_rot13" + "str_shuffle" + "str_split" + "str_starts_with" + "str_word_count" + "strcasecmp" + "strchr" + "strcmp" + "strcoll" + "strcspn" + "stream_bucket_append" + "stream_bucket_make_writeable" + "stream_bucket_new" + "stream_bucket_prepend" + "stream_context_create" + "stream_context_get_default" + "stream_context_get_options" + "stream_context_get_params" + "stream_context_set_default" + "stream_context_set_option" + "stream_context_set_params" + "stream_copy_to_stream" + "stream_filter_append" + "stream_filter_prepend" + "stream_filter_register" + "stream_filter_remove" + "stream_get_contents" + "stream_get_filters" + "stream_get_line" + "stream_get_meta_data" + "stream_get_transports" + "stream_get_wrappers" + "stream_is_local" + "stream_isatty" + "stream_notification_callback" + "stream_register_wrapper" + "stream_resolve_include_path" + "stream_select" + "stream_set_blocking" + "stream_set_chunk_size" + "stream_set_read_buffer" + "stream_set_timeout" + "stream_set_write_buffer" + "stream_socket_accept" + "stream_socket_client" + "stream_socket_enable_crypto" + "stream_socket_get_name" + "stream_socket_pair" + "stream_socket_recvfrom" + "stream_socket_sendto" + "stream_socket_server" + "stream_socket_shutdown" + "stream_supports_lock" + "stream_wrapper_register" + "stream_wrapper_restore" + "stream_wrapper_unregister" + "strftime" + "strip_tags" + "stripcslashes" + "stripos" + "stripslashes" + "stristr" + "strlen" + "strnatcasecmp" + "strnatcmp" + "strncasecmp" + "strncmp" + "strpbrk" + "strpos" + "strptime" + "strrchr" + "strrev" + "strripos" + "strrpos" + "strspn" + "strstr" + "strtok" + "strtolower" + "strtotime" + "strtoupper" + "strtr" + "strval" + "substr" + "substr_compare" + "substr_count" + "substr_replace" + "symlink" + "sys_get_temp_dir" + "sys_getloadavg" + "syslog" + "system" + "tan" + "tanh" + "tempnam" + "time" + "time_nanosleep" + "time_sleep_until" + "timezone_abbreviations_list" + "timezone_identifiers_list" + "timezone_location_get" + "timezone_name_from_abbr" + "timezone_name_get" + "timezone_offset_get" + "timezone_open" + "timezone_transitions_get" + "timezone_version_get" + "tmpfile" + "token_get_all" + "token_name" + "touch" + "trait_exists" + "trigger_error" + "trim" + "uasort" + "ucfirst" + "ucwords" + "uksort" + "umask" + "uniqid" + "unlink" + "unpack" + "unregister_tick_function" + "unserialize" + "unset" + "urldecode" + "urlencode" + "user_error" + "usleep" + "usort" + "var_dump" + "var_export" + "version_compare" + "vfprintf" + "vprintf" + "vsprintf" + "wordwrap" + "zend_thread_id" + "zend_version") + (cubrid + "cubrid_affected_rows" + "cubrid_bind" + "cubrid_client_encoding" + "cubrid_close" + "cubrid_close_prepare" + "cubrid_close_request" + "cubrid_col_get" + "cubrid_col_size" + "cubrid_column_names" + "cubrid_column_types" + "cubrid_commit" + "cubrid_connect" + "cubrid_connect_with_url" + "cubrid_current_oid" + "cubrid_data_seek" + "cubrid_db_name" + "cubrid_disconnect" + "cubrid_drop" + "cubrid_errno" + "cubrid_error" + "cubrid_error_code" + "cubrid_error_code_facility" + "cubrid_error_msg" + "cubrid_execute" + "cubrid_fetch" + "cubrid_fetch_array" + "cubrid_fetch_assoc" + "cubrid_fetch_field" + "cubrid_fetch_lengths" + "cubrid_fetch_object" + "cubrid_fetch_row" + "cubrid_field_flags" + "cubrid_field_len" + "cubrid_field_name" + "cubrid_field_seek" + "cubrid_field_table" + "cubrid_field_type" + "cubrid_free_result" + "cubrid_get" + "cubrid_get_autocommit" + "cubrid_get_charset" + "cubrid_get_class_name" + "cubrid_get_client_info" + "cubrid_get_db_parameter" + "cubrid_get_query_timeout" + "cubrid_get_server_info" + "cubrid_insert_id" + "cubrid_is_instance" + "cubrid_list_dbs" + "cubrid_load_from_glo" + "cubrid_lob2_bind" + "cubrid_lob2_close" + "cubrid_lob2_export" + "cubrid_lob2_import" + "cubrid_lob2_new" + "cubrid_lob2_read" + "cubrid_lob2_seek" + "cubrid_lob2_seek64" + "cubrid_lob2_size" + "cubrid_lob2_size64" + "cubrid_lob2_tell" + "cubrid_lob2_tell64" + "cubrid_lob2_write" + "cubrid_lob_close" + "cubrid_lob_export" + "cubrid_lob_get" + "cubrid_lob_send" + "cubrid_lob_size" + "cubrid_lock_read" + "cubrid_lock_write" + "cubrid_move_cursor" + "cubrid_new_glo" + "cubrid_next_result" + "cubrid_num_cols" + "cubrid_num_fields" + "cubrid_num_rows" + "cubrid_pconnect" + "cubrid_pconnect_with_url" + "cubrid_ping" + "cubrid_prepare" + "cubrid_put" + "cubrid_query" + "cubrid_real_escape_string" + "cubrid_result" + "cubrid_rollback" + "cubrid_save_to_glo" + "cubrid_schema" + "cubrid_send_glo" + "cubrid_seq_drop" + "cubrid_seq_insert" + "cubrid_seq_put" + "cubrid_set_add" + "cubrid_set_autocommit" + "cubrid_set_db_parameter" + "cubrid_set_drop" + "cubrid_set_query_timeout" + "cubrid_unbuffered_query" + "cubrid_version") + (curl + "curl_close" + "curl_copy_handle" + "curl_errno" + "curl_error" + "curl_escape" + "curl_exec" + "curl_file_create" + "curl_getinfo" + "curl_init" + "curl_multi_add_handle" + "curl_multi_close" + "curl_multi_errno" + "curl_multi_exec" + "curl_multi_getcontent" + "curl_multi_info_read" + "curl_multi_init" + "curl_multi_remove_handle" + "curl_multi_select" + "curl_multi_setopt" + "curl_multi_strerror" + "curl_pause" + "curl_reset" + "curl_setopt" + "curl_setopt_array" + "curl_share_close" + "curl_share_errno" + "curl_share_init" + "curl_share_setopt" + "curl_share_strerror" + "curl_strerror" + "curl_unescape" + "curl_version") + (dba + "dba_close" + "dba_delete" + "dba_exists" + "dba_fetch" + "dba_firstkey" + "dba_handlers" + "dba_insert" + "dba_key_split" + "dba_list" + "dba_nextkey" + "dba_open" + "dba_optimize" + "dba_popen" + "dba_replace" + "dba_sync") + (dbase + "dbase_add_record" + "dbase_close" + "dbase_create" + "dbase_delete_record" + "dbase_get_header_info" + "dbase_get_record" + "dbase_get_record_with_names" + "dbase_numfields" + "dbase_numrecords" + "dbase_open" + "dbase_pack" + "dbase_replace_record") + (dio + "dio_close" + "dio_fcntl" + "dio_open" + "dio_read" + "dio_seek" + "dio_stat" + "dio_tcsetattr" + "dio_truncate" + "dio_write") + (eio + "eio_busy" + "eio_cancel" + "eio_chmod" + "eio_chown" + "eio_close" + "eio_custom" + "eio_dup2" + "eio_event_loop" + "eio_fallocate" + "eio_fchmod" + "eio_fchown" + "eio_fdatasync" + "eio_fstat" + "eio_fstatvfs" + "eio_fsync" + "eio_ftruncate" + "eio_futime" + "eio_get_event_stream" + "eio_get_last_error" + "eio_grp" + "eio_grp_add" + "eio_grp_cancel" + "eio_grp_limit" + "eio_init" + "eio_link" + "eio_lstat" + "eio_mkdir" + "eio_mknod" + "eio_nop" + "eio_npending" + "eio_nready" + "eio_nreqs" + "eio_nthreads" + "eio_open" + "eio_poll" + "eio_read" + "eio_readahead" + "eio_readdir" + "eio_readlink" + "eio_realpath" + "eio_rename" + "eio_rmdir" + "eio_seek" + "eio_sendfile" + "eio_set_max_idle" + "eio_set_max_parallel" + "eio_set_max_poll_reqs" + "eio_set_max_poll_time" + "eio_set_min_parallel" + "eio_stat" + "eio_statvfs" + "eio_symlink" + "eio_sync" + "eio_sync_file_range" + "eio_syncfs" + "eio_truncate" + "eio_unlink" + "eio_utime" + "eio_write") + (enchant + "enchant_broker_describe" + "enchant_broker_dict_exists" + "enchant_broker_free" + "enchant_broker_free_dict" + "enchant_broker_get_dict_path" + "enchant_broker_get_error" + "enchant_broker_init" + "enchant_broker_list_dicts" + "enchant_broker_request_dict" + "enchant_broker_request_pwl_dict" + "enchant_broker_set_dict_path" + "enchant_broker_set_ordering" + "enchant_dict_add" + "enchant_dict_add_to_personal" + "enchant_dict_add_to_session" + "enchant_dict_check" + "enchant_dict_describe" + "enchant_dict_get_error" + "enchant_dict_is_added" + "enchant_dict_is_in_session" + "enchant_dict_quick_check" + "enchant_dict_store_replacement" + "enchant_dict_suggest") + (exif + "exif_imagetype" + "exif_read_data" + "exif_tagname" + "exif_thumbnail" + "read_exif_data") + (expect + "expect_expectl" + "expect_popen") + (fann + "fann_cascadetrain_on_data" + "fann_cascadetrain_on_file" + "fann_clear_scaling_params" + "fann_copy" + "fann_create_from_file" + "fann_create_shortcut" + "fann_create_shortcut_array" + "fann_create_sparse" + "fann_create_sparse_array" + "fann_create_standard" + "fann_create_standard_array" + "fann_create_train" + "fann_create_train_from_callback" + "fann_descale_input" + "fann_descale_output" + "fann_descale_train" + "fann_destroy" + "fann_destroy_train" + "fann_duplicate_train_data" + "fann_get_MSE" + "fann_get_activation_function" + "fann_get_activation_steepness" + "fann_get_bias_array" + "fann_get_bit_fail" + "fann_get_bit_fail_limit" + "fann_get_cascade_activation_functions" + "fann_get_cascade_activation_functions_count" + "fann_get_cascade_activation_steepnesses" + "fann_get_cascade_activation_steepnesses_count" + "fann_get_cascade_candidate_change_fraction" + "fann_get_cascade_candidate_limit" + "fann_get_cascade_candidate_stagnation_epochs" + "fann_get_cascade_max_cand_epochs" + "fann_get_cascade_max_out_epochs" + "fann_get_cascade_min_cand_epochs" + "fann_get_cascade_min_out_epochs" + "fann_get_cascade_num_candidate_groups" + "fann_get_cascade_num_candidates" + "fann_get_cascade_output_change_fraction" + "fann_get_cascade_output_stagnation_epochs" + "fann_get_cascade_weight_multiplier" + "fann_get_connection_array" + "fann_get_connection_rate" + "fann_get_errno" + "fann_get_errstr" + "fann_get_layer_array" + "fann_get_learning_momentum" + "fann_get_learning_rate" + "fann_get_network_type" + "fann_get_num_input" + "fann_get_num_layers" + "fann_get_num_output" + "fann_get_quickprop_decay" + "fann_get_quickprop_mu" + "fann_get_rprop_decrease_factor" + "fann_get_rprop_delta_max" + "fann_get_rprop_delta_min" + "fann_get_rprop_delta_zero" + "fann_get_rprop_increase_factor" + "fann_get_sarprop_step_error_shift" + "fann_get_sarprop_step_error_threshold_factor" + "fann_get_sarprop_temperature" + "fann_get_sarprop_weight_decay_shift" + "fann_get_total_connections" + "fann_get_total_neurons" + "fann_get_train_error_function" + "fann_get_train_stop_function" + "fann_get_training_algorithm" + "fann_init_weights" + "fann_length_train_data" + "fann_merge_train_data" + "fann_num_input_train_data" + "fann_num_output_train_data" + "fann_print_error" + "fann_randomize_weights" + "fann_read_train_from_file" + "fann_reset_MSE" + "fann_reset_errno" + "fann_reset_errstr" + "fann_run" + "fann_save" + "fann_save_train" + "fann_scale_input" + "fann_scale_input_train_data" + "fann_scale_output" + "fann_scale_output_train_data" + "fann_scale_train" + "fann_scale_train_data" + "fann_set_activation_function" + "fann_set_activation_function_hidden" + "fann_set_activation_function_layer" + "fann_set_activation_function_output" + "fann_set_activation_steepness" + "fann_set_activation_steepness_hidden" + "fann_set_activation_steepness_layer" + "fann_set_activation_steepness_output" + "fann_set_bit_fail_limit" + "fann_set_callback" + "fann_set_cascade_activation_functions" + "fann_set_cascade_activation_steepnesses" + "fann_set_cascade_candidate_change_fraction" + "fann_set_cascade_candidate_limit" + "fann_set_cascade_candidate_stagnation_epochs" + "fann_set_cascade_max_cand_epochs" + "fann_set_cascade_max_out_epochs" + "fann_set_cascade_min_cand_epochs" + "fann_set_cascade_min_out_epochs" + "fann_set_cascade_num_candidate_groups" + "fann_set_cascade_output_change_fraction" + "fann_set_cascade_output_stagnation_epochs" + "fann_set_cascade_weight_multiplier" + "fann_set_error_log" + "fann_set_input_scaling_params" + "fann_set_learning_momentum" + "fann_set_learning_rate" + "fann_set_output_scaling_params" + "fann_set_quickprop_decay" + "fann_set_quickprop_mu" + "fann_set_rprop_decrease_factor" + "fann_set_rprop_delta_max" + "fann_set_rprop_delta_min" + "fann_set_rprop_delta_zero" + "fann_set_rprop_increase_factor" + "fann_set_sarprop_step_error_shift" + "fann_set_sarprop_step_error_threshold_factor" + "fann_set_sarprop_temperature" + "fann_set_sarprop_weight_decay_shift" + "fann_set_scaling_params" + "fann_set_train_error_function" + "fann_set_train_stop_function" + "fann_set_training_algorithm" + "fann_set_weight" + "fann_set_weight_array" + "fann_shuffle_train_data" + "fann_subset_train_data" + "fann_test" + "fann_test_data" + "fann_train" + "fann_train_epoch" + "fann_train_on_data" + "fann_train_on_file") + (fdf + "fdf_add_doc_javascript" + "fdf_add_template" + "fdf_close" + "fdf_create" + "fdf_enum_values" + "fdf_errno" + "fdf_error" + "fdf_get_ap" + "fdf_get_attachment" + "fdf_get_encoding" + "fdf_get_file" + "fdf_get_flags" + "fdf_get_opt" + "fdf_get_status" + "fdf_get_value" + "fdf_get_version" + "fdf_header" + "fdf_next_field_name" + "fdf_open" + "fdf_open_string" + "fdf_remove_item" + "fdf_save" + "fdf_save_string" + "fdf_set_ap" + "fdf_set_encoding" + "fdf_set_file" + "fdf_set_flags" + "fdf_set_javascript_action" + "fdf_set_on_import_javascript" + "fdf_set_opt" + "fdf_set_status" + "fdf_set_submit_form_action" + "fdf_set_target_frame" + "fdf_set_value" + "fdf_set_version") + (fpm + "fpm_get_status") + (ftp + "ftp_alloc" + "ftp_append" + "ftp_cdup" + "ftp_chdir" + "ftp_chmod" + "ftp_close" + "ftp_connect" + "ftp_delete" + "ftp_exec" + "ftp_fget" + "ftp_fput" + "ftp_get" + "ftp_get_option" + "ftp_login" + "ftp_mdtm" + "ftp_mkdir" + "ftp_mlsd" + "ftp_nb_continue" + "ftp_nb_fget" + "ftp_nb_fput" + "ftp_nb_get" + "ftp_nb_put" + "ftp_nlist" + "ftp_pasv" + "ftp_put" + "ftp_pwd" + "ftp_quit" + "ftp_raw" + "ftp_rawlist" + "ftp_rename" + "ftp_rmdir" + "ftp_set_option" + "ftp_site" + "ftp_size" + "ftp_ssl_connect" + "ftp_systype") + (gd + "gd_info" + "getimagesize" + "getimagesizefromstring" + "image2wbmp" + "image_type_to_extension" + "image_type_to_mime_type" + "imageaffine" + "imageaffinematrixconcat" + "imageaffinematrixget" + "imagealphablending" + "imageantialias" + "imagearc" + "imageavif" + "imagebmp" + "imagechar" + "imagecharup" + "imagecolorallocate" + "imagecolorallocatealpha" + "imagecolorat" + "imagecolorclosest" + "imagecolorclosestalpha" + "imagecolorclosesthwb" + "imagecolordeallocate" + "imagecolorexact" + "imagecolorexactalpha" + "imagecolormatch" + "imagecolorresolve" + "imagecolorresolvealpha" + "imagecolorset" + "imagecolorsforindex" + "imagecolorstotal" + "imagecolortransparent" + "imageconvolution" + "imagecopy" + "imagecopymerge" + "imagecopymergegray" + "imagecopyresampled" + "imagecopyresized" + "imagecreate" + "imagecreatefromavif" + "imagecreatefrombmp" + "imagecreatefromgd" + "imagecreatefromgd2" + "imagecreatefromgd2part" + "imagecreatefromgif" + "imagecreatefromjpeg" + "imagecreatefrompng" + "imagecreatefromstring" + "imagecreatefromtga" + "imagecreatefromwbmp" + "imagecreatefromwebp" + "imagecreatefromxbm" + "imagecreatefromxpm" + "imagecreatetruecolor" + "imagecrop" + "imagecropauto" + "imagedashedline" + "imagedestroy" + "imageellipse" + "imagefill" + "imagefilledarc" + "imagefilledellipse" + "imagefilledpolygon" + "imagefilledrectangle" + "imagefilltoborder" + "imagefilter" + "imageflip" + "imagefontheight" + "imagefontwidth" + "imageftbbox" + "imagefttext" + "imagegammacorrect" + "imagegd" + "imagegd2" + "imagegetclip" + "imagegetinterpolation" + "imagegif" + "imagegrabscreen" + "imagegrabwindow" + "imageinterlace" + "imageistruecolor" + "imagejpeg" + "imagelayereffect" + "imageline" + "imageloadfont" + "imageopenpolygon" + "imagepalettecopy" + "imagepalettetotruecolor" + "imagepng" + "imagepolygon" + "imagerectangle" + "imageresolution" + "imagerotate" + "imagesavealpha" + "imagescale" + "imagesetbrush" + "imagesetclip" + "imagesetinterpolation" + "imagesetpixel" + "imagesetstyle" + "imagesetthickness" + "imagesettile" + "imagestring" + "imagestringup" + "imagesx" + "imagesy" + "imagetruecolortopalette" + "imagettfbbox" + "imagettftext" + "imagetypes" + "imagewbmp" + "imagewebp" + "imagexbm" + "iptcembed" + "iptcparse") + (gearman + "gearman_job_handle" + "gearman_job_status") + (geoip + "geoip_asnum_by_name" + "geoip_continent_code_by_name" + "geoip_country_code3_by_name" + "geoip_country_code_by_name" + "geoip_country_name_by_name" + "geoip_database_info" + "geoip_db_avail" + "geoip_db_filename" + "geoip_db_get_all_info" + "geoip_domain_by_name" + "geoip_id_by_name" + "geoip_isp_by_name" + "geoip_netspeedcell_by_name" + "geoip_org_by_name" + "geoip_record_by_name" + "geoip_region_by_name" + "geoip_region_name_by_code" + "geoip_setup_custom_directory" + "geoip_time_zone_by_country_and_region") + (gettext + "bind_textdomain_codeset" + "bindtextdomain" + "dcgettext" + "dcngettext" + "dgettext" + "dngettext" + "gettext" + "ngettext" + "textdomain") + (gmp + "gmp_abs" + "gmp_add" + "gmp_and" + "gmp_binomial" + "gmp_clrbit" + "gmp_cmp" + "gmp_com" + "gmp_div" + "gmp_div_q" + "gmp_div_qr" + "gmp_div_r" + "gmp_divexact" + "gmp_export" + "gmp_fact" + "gmp_gcd" + "gmp_gcdext" + "gmp_hamdist" + "gmp_import" + "gmp_init" + "gmp_intval" + "gmp_invert" + "gmp_jacobi" + "gmp_kronecker" + "gmp_lcm" + "gmp_legendre" + "gmp_mod" + "gmp_mul" + "gmp_neg" + "gmp_nextprime" + "gmp_or" + "gmp_perfect_power" + "gmp_perfect_square" + "gmp_popcount" + "gmp_pow" + "gmp_powm" + "gmp_prob_prime" + "gmp_random" + "gmp_random_bits" + "gmp_random_range" + "gmp_random_seed" + "gmp_root" + "gmp_rootrem" + "gmp_scan0" + "gmp_scan1" + "gmp_setbit" + "gmp_sign" + "gmp_sqrt" + "gmp_sqrtrem" + "gmp_strval" + "gmp_sub" + "gmp_testbit" + "gmp_xor") + (gnupg + "gnupg_adddecryptkey" + "gnupg_addencryptkey" + "gnupg_addsignkey" + "gnupg_cleardecryptkeys" + "gnupg_clearencryptkeys" + "gnupg_clearsignkeys" + "gnupg_decrypt" + "gnupg_decryptverify" + "gnupg_deletekey" + "gnupg_encrypt" + "gnupg_encryptsign" + "gnupg_export" + "gnupg_getengineinfo" + "gnupg_geterror" + "gnupg_geterrorinfo" + "gnupg_getprotocol" + "gnupg_gettrustlist" + "gnupg_import" + "gnupg_init" + "gnupg_keyinfo" + "gnupg_listsignatures" + "gnupg_setarmor" + "gnupg_seterrormode" + "gnupg_setsignmode" + "gnupg_sign" + "gnupg_verify") + (ibase + "fbird_add_user" + "fbird_affected_rows" + "fbird_backup" + "fbird_blob_add" + "fbird_blob_cancel" + "fbird_blob_close" + "fbird_blob_create" + "fbird_blob_echo" + "fbird_blob_get" + "fbird_blob_import" + "fbird_blob_info" + "fbird_blob_open" + "fbird_close" + "fbird_commit" + "fbird_commit_ret" + "fbird_connect" + "fbird_db_info" + "fbird_delete_user" + "fbird_drop_db" + "fbird_errcode" + "fbird_errmsg" + "fbird_execute" + "fbird_fetch_assoc" + "fbird_fetch_object" + "fbird_fetch_row" + "fbird_field_info" + "fbird_free_event_handler" + "fbird_free_query" + "fbird_free_result" + "fbird_gen_id" + "fbird_maintain_db" + "fbird_modify_user" + "fbird_name_result" + "fbird_num_fields" + "fbird_num_params" + "fbird_param_info" + "fbird_pconnect" + "fbird_prepare" + "fbird_query" + "fbird_restore" + "fbird_rollback" + "fbird_rollback_ret" + "fbird_server_info" + "fbird_service_attach" + "fbird_service_detach" + "fbird_set_event_handler" + "fbird_trans" + "fbird_wait_event" + "ibase_add_user" + "ibase_affected_rows" + "ibase_backup" + "ibase_blob_add" + "ibase_blob_cancel" + "ibase_blob_close" + "ibase_blob_create" + "ibase_blob_echo" + "ibase_blob_get" + "ibase_blob_import" + "ibase_blob_info" + "ibase_blob_open" + "ibase_close" + "ibase_commit" + "ibase_commit_ret" + "ibase_connect" + "ibase_db_info" + "ibase_delete_user" + "ibase_drop_db" + "ibase_errcode" + "ibase_errmsg" + "ibase_execute" + "ibase_fetch_assoc" + "ibase_fetch_object" + "ibase_fetch_row" + "ibase_field_info" + "ibase_free_event_handler" + "ibase_free_query" + "ibase_free_result" + "ibase_gen_id" + "ibase_maintain_db" + "ibase_modify_user" + "ibase_name_result" + "ibase_num_fields" + "ibase_num_params" + "ibase_param_info" + "ibase_pconnect" + "ibase_prepare" + "ibase_query" + "ibase_restore" + "ibase_rollback" + "ibase_rollback_ret" + "ibase_server_info" + "ibase_service_attach" + "ibase_service_detach" + "ibase_set_event_handler" + "ibase_trans" + "ibase_wait_event") + (ibm_db2 + "db2_autocommit" + "db2_bind_param" + "db2_client_info" + "db2_close" + "db2_column_privileges" + "db2_columns" + "db2_commit" + "db2_conn_error" + "db2_conn_errormsg" + "db2_connect" + "db2_cursor_type" + "db2_escape_string" + "db2_exec" + "db2_execute" + "db2_fetch_array" + "db2_fetch_assoc" + "db2_fetch_both" + "db2_fetch_object" + "db2_fetch_row" + "db2_field_display_size" + "db2_field_name" + "db2_field_num" + "db2_field_precision" + "db2_field_scale" + "db2_field_type" + "db2_field_width" + "db2_foreign_keys" + "db2_free_result" + "db2_free_stmt" + "db2_get_option" + "db2_last_insert_id" + "db2_lob_read" + "db2_next_result" + "db2_num_fields" + "db2_num_rows" + "db2_pclose" + "db2_pconnect" + "db2_prepare" + "db2_primary_keys" + "db2_procedure_columns" + "db2_procedures" + "db2_result" + "db2_rollback" + "db2_server_info" + "db2_set_option" + "db2_special_columns" + "db2_statistics" + "db2_stmt_error" + "db2_stmt_errormsg" + "db2_table_privileges" + "db2_tables") + (iconv + "iconv" + "iconv_get_encoding" + "iconv_mime_decode" + "iconv_mime_decode_headers" + "iconv_mime_encode" + "iconv_set_encoding" + "iconv_strlen" + "iconv_strpos" + "iconv_strrpos" + "iconv_substr") + (igbinary + "igbinary_serialize" + "igbinary_unserialize") + (imap + "imap_8bit" + "imap_alerts" + "imap_append" + "imap_base64" + "imap_binary" + "imap_body" + "imap_bodystruct" + "imap_check" + "imap_clearflag_full" + "imap_close" + "imap_create" + "imap_createmailbox" + "imap_delete" + "imap_deletemailbox" + "imap_errors" + "imap_expunge" + "imap_fetch_overview" + "imap_fetchbody" + "imap_fetchheader" + "imap_fetchmime" + "imap_fetchstructure" + "imap_fetchtext" + "imap_gc" + "imap_get_quota" + "imap_get_quotaroot" + "imap_getacl" + "imap_getmailboxes" + "imap_getsubscribed" + "imap_header" + "imap_headerinfo" + "imap_headers" + "imap_last_error" + "imap_list" + "imap_listmailbox" + "imap_listscan" + "imap_listsubscribed" + "imap_lsub" + "imap_mail" + "imap_mail_compose" + "imap_mail_copy" + "imap_mail_move" + "imap_mailboxmsginfo" + "imap_mime_header_decode" + "imap_msgno" + "imap_mutf7_to_utf8" + "imap_num_msg" + "imap_num_recent" + "imap_open" + "imap_ping" + "imap_qprint" + "imap_rename" + "imap_renamemailbox" + "imap_reopen" + "imap_rfc822_parse_adrlist" + "imap_rfc822_parse_headers" + "imap_rfc822_write_address" + "imap_savebody" + "imap_scan" + "imap_scanmailbox" + "imap_search" + "imap_set_quota" + "imap_setacl" + "imap_setflag_full" + "imap_sort" + "imap_status" + "imap_subscribe" + "imap_thread" + "imap_timeout" + "imap_uid" + "imap_undelete" + "imap_unsubscribe" + "imap_utf7_decode" + "imap_utf7_encode" + "imap_utf8" + "imap_utf8_to_mutf7") + (inotify + "inotify_add_watch" + "inotify_init" + "inotify_queue_len" + "inotify_read" + "inotify_rm_watch") + (intl + "collator_asort" + "collator_compare" + "collator_create" + "collator_get_attribute" + "collator_get_error_code" + "collator_get_error_message" + "collator_get_locale" + "collator_get_sort_key" + "collator_get_strength" + "collator_set_attribute" + "collator_set_strength" + "collator_sort" + "collator_sort_with_sort_keys" + "datefmt_create" + "datefmt_format" + "datefmt_format_object" + "datefmt_get_calendar" + "datefmt_get_calendar_object" + "datefmt_get_datetype" + "datefmt_get_error_code" + "datefmt_get_error_message" + "datefmt_get_locale" + "datefmt_get_pattern" + "datefmt_get_timetype" + "datefmt_get_timezone" + "datefmt_get_timezone_id" + "datefmt_is_lenient" + "datefmt_localtime" + "datefmt_parse" + "datefmt_set_calendar" + "datefmt_set_lenient" + "datefmt_set_pattern" + "datefmt_set_timezone" + "grapheme_extract" + "grapheme_stripos" + "grapheme_stristr" + "grapheme_strlen" + "grapheme_strpos" + "grapheme_strripos" + "grapheme_strrpos" + "grapheme_strstr" + "grapheme_substr" + "idn_to_ascii" + "idn_to_utf8" + "intl_error_name" + "intl_get_error_code" + "intl_get_error_message" + "intl_is_failure" + "intlcal_get_error_code" + "intlcal_get_error_message" + "intltz_count_equivalent_ids" + "intltz_create_default" + "intltz_create_enumeration" + "intltz_create_time_zone" + "intltz_create_time_zone_id_enumeration" + "intltz_from_date_time_zone" + "intltz_get_canonical_id" + "intltz_get_display_name" + "intltz_get_dst_savings" + "intltz_get_equivalent_id" + "intltz_get_error_code" + "intltz_get_error_message" + "intltz_get_gmt" + "intltz_get_id" + "intltz_get_id_for_windows_id" + "intltz_get_offset" + "intltz_get_raw_offset" + "intltz_get_region" + "intltz_get_tz_data_version" + "intltz_get_unknown" + "intltz_get_windows_id" + "intltz_has_same_rules" + "intltz_to_date_time_zone" + "intltz_use_daylight_time" + "locale_accept_from_http" + "locale_canonicalize" + "locale_compose" + "locale_filter_matches" + "locale_get_all_variants" + "locale_get_default" + "locale_get_display_language" + "locale_get_display_name" + "locale_get_display_region" + "locale_get_display_script" + "locale_get_display_variant" + "locale_get_keywords" + "locale_get_primary_language" + "locale_get_region" + "locale_get_script" + "locale_lookup" + "locale_parse" + "locale_set_default" + "msgfmt_create" + "msgfmt_format" + "msgfmt_format_message" + "msgfmt_get_error_code" + "msgfmt_get_error_message" + "msgfmt_get_locale" + "msgfmt_get_pattern" + "msgfmt_parse" + "msgfmt_parse_message" + "msgfmt_set_pattern" + "normalizer_get_raw_decomposition" + "normalizer_is_normalized" + "normalizer_normalize" + "numfmt_create" + "numfmt_format" + "numfmt_format_currency" + "numfmt_get_attribute" + "numfmt_get_error_code" + "numfmt_get_error_message" + "numfmt_get_locale" + "numfmt_get_pattern" + "numfmt_get_symbol" + "numfmt_get_text_attribute" + "numfmt_parse" + "numfmt_parse_currency" + "numfmt_set_attribute" + "numfmt_set_pattern" + "numfmt_set_symbol" + "numfmt_set_text_attribute" + "resourcebundle_count" + "resourcebundle_create" + "resourcebundle_get" + "resourcebundle_get_error_code" + "resourcebundle_get_error_message" + "resourcebundle_locales" + "transliterator_create" + "transliterator_create_from_rules" + "transliterator_create_inverse" + "transliterator_get_error_code" + "transliterator_get_error_message" + "transliterator_list_ids" + "transliterator_transliterate") + (language + "__halt_compiler" + "array" + "assert" + "echo" + "empty" + "isset" + "print") + (ldap + "ldap_8859_to_t61" + "ldap_add" + "ldap_add_ext" + "ldap_bind" + "ldap_bind_ext" + "ldap_close" + "ldap_compare" + "ldap_connect" + "ldap_control_paged_result" + "ldap_control_paged_result_response" + "ldap_count_entries" + "ldap_count_references" + "ldap_delete" + "ldap_delete_ext" + "ldap_dn2ufn" + "ldap_err2str" + "ldap_errno" + "ldap_error" + "ldap_escape" + "ldap_exop" + "ldap_exop_passwd" + "ldap_exop_refresh" + "ldap_exop_whoami" + "ldap_explode_dn" + "ldap_first_attribute" + "ldap_first_entry" + "ldap_first_reference" + "ldap_free_result" + "ldap_get_attributes" + "ldap_get_dn" + "ldap_get_entries" + "ldap_get_option" + "ldap_get_values" + "ldap_get_values_len" + "ldap_list" + "ldap_mod_add" + "ldap_mod_add_ext" + "ldap_mod_del" + "ldap_mod_del_ext" + "ldap_mod_replace" + "ldap_mod_replace_ext" + "ldap_modify" + "ldap_modify_batch" + "ldap_next_attribute" + "ldap_next_entry" + "ldap_next_reference" + "ldap_parse_exop" + "ldap_parse_reference" + "ldap_parse_result" + "ldap_read" + "ldap_rename" + "ldap_rename_ext" + "ldap_sasl_bind" + "ldap_search" + "ldap_set_option" + "ldap_set_rebind_proc" + "ldap_sort" + "ldap_start_tls" + "ldap_t61_to_8859" + "ldap_unbind") + (libxml + "libxml_clear_errors" + "libxml_disable_entity_loader" + "libxml_get_errors" + "libxml_get_last_error" + "libxml_set_external_entity_loader" + "libxml_set_streams_context" + "libxml_use_internal_errors") + (lzf + "lzf_compress" + "lzf_decompress" + "lzf_optimized_for") + (mailparse + "mailparse_determine_best_xfer_encoding" + "mailparse_msg_create" + "mailparse_msg_extract_part" + "mailparse_msg_extract_part_file" + "mailparse_msg_extract_whole_part_file" + "mailparse_msg_free" + "mailparse_msg_get_part" + "mailparse_msg_get_part_data" + "mailparse_msg_get_structure" + "mailparse_msg_parse" + "mailparse_msg_parse_file" + "mailparse_rfc822_parse_addresses" + "mailparse_stream_encode" + "mailparse_uudecode_all") + (mbstring + "mb_check_encoding" + "mb_chr" + "mb_convert_case" + "mb_convert_encoding" + "mb_convert_kana" + "mb_convert_variables" + "mb_decode_mimeheader" + "mb_decode_numericentity" + "mb_detect_encoding" + "mb_detect_order" + "mb_encode_mimeheader" + "mb_encode_numericentity" + "mb_encoding_aliases" + "mb_ereg" + "mb_ereg_match" + "mb_ereg_replace" + "mb_ereg_replace_callback" + "mb_ereg_search" + "mb_ereg_search_getpos" + "mb_ereg_search_getregs" + "mb_ereg_search_init" + "mb_ereg_search_pos" + "mb_ereg_search_regs" + "mb_ereg_search_setpos" + "mb_eregi" + "mb_eregi_replace" + "mb_get_info" + "mb_http_input" + "mb_http_output" + "mb_internal_encoding" + "mb_language" + "mb_list_encodings" + "mb_ord" + "mb_output_handler" + "mb_parse_str" + "mb_preferred_mime_name" + "mb_regex_encoding" + "mb_regex_set_options" + "mb_scrub" + "mb_send_mail" + "mb_split" + "mb_str_split" + "mb_strcut" + "mb_strimwidth" + "mb_stripos" + "mb_stristr" + "mb_strlen" + "mb_strpos" + "mb_strrchr" + "mb_strrichr" + "mb_strripos" + "mb_strrpos" + "mb_strstr" + "mb_strtolower" + "mb_strtoupper" + "mb_strwidth" + "mb_substitute_character" + "mb_substr" + "mb_substr_count") + (memcache + "memcache_debug") + (mongodb + "MongoDB\\BSON\\fromJSON" + "MongoDB\\BSON\\fromPHP" + "MongoDB\\BSON\\toCanonicalExtendedJSON" + "MongoDB\\BSON\\toJSON" + "MongoDB\\BSON\\toPHP" + "MongoDB\\BSON\\toRelaxedExtendedJSON" + "MongoDB\\Driver\\Monitoring\\addSubscriber" + "MongoDB\\Driver\\Monitoring\\removeSubscriber") + (mqseries + "mqseries_back" + "mqseries_begin" + "mqseries_close" + "mqseries_cmit" + "mqseries_conn" + "mqseries_connx" + "mqseries_disc" + "mqseries_get" + "mqseries_inq" + "mqseries_open" + "mqseries_put" + "mqseries_put1" + "mqseries_set" + "mqseries_strerror") + (mysql-obsolete + "expression" + "getSession" + "mysql_affected_rows" + "mysql_client_encoding" + "mysql_close" + "mysql_connect" + "mysql_create_db" + "mysql_data_seek" + "mysql_db_name" + "mysql_db_query" + "mysql_drop_db" + "mysql_errno" + "mysql_error" + "mysql_escape_string" + "mysql_fetch_array" + "mysql_fetch_assoc" + "mysql_fetch_field" + "mysql_fetch_lengths" + "mysql_fetch_object" + "mysql_fetch_row" + "mysql_field_flags" + "mysql_field_len" + "mysql_field_name" + "mysql_field_seek" + "mysql_field_table" + "mysql_field_type" + "mysql_free_result" + "mysql_get_client_info" + "mysql_get_host_info" + "mysql_get_proto_info" + "mysql_get_server_info" + "mysql_info" + "mysql_insert_id" + "mysql_list_dbs" + "mysql_list_fields" + "mysql_list_processes" + "mysql_list_tables" + "mysql_num_fields" + "mysql_num_rows" + "mysql_pconnect" + "mysql_ping" + "mysql_query" + "mysql_real_escape_string" + "mysql_result" + "mysql_select_db" + "mysql_set_charset" + "mysql_stat" + "mysql_tablename" + "mysql_thread_id" + "mysql_unbuffered_query") + (mysqli + "mysqli_affected_rows" + "mysqli_autocommit" + "mysqli_begin_transaction" + "mysqli_change_user" + "mysqli_character_set_name" + "mysqli_close" + "mysqli_commit" + "mysqli_connect" + "mysqli_connect_errno" + "mysqli_connect_error" + "mysqli_data_seek" + "mysqli_debug" + "mysqli_dump_debug_info" + "mysqli_embedded_server_end" + "mysqli_embedded_server_start" + "mysqli_errno" + "mysqli_error" + "mysqli_error_list" + "mysqli_escape_string" + "mysqli_execute" + "mysqli_fetch_all" + "mysqli_fetch_array" + "mysqli_fetch_assoc" + "mysqli_fetch_column" + "mysqli_fetch_field" + "mysqli_fetch_field_direct" + "mysqli_fetch_fields" + "mysqli_fetch_lengths" + "mysqli_fetch_object" + "mysqli_fetch_row" + "mysqli_field_count" + "mysqli_field_seek" + "mysqli_field_tell" + "mysqli_free_result" + "mysqli_get_charset" + "mysqli_get_client_info" + "mysqli_get_client_stats" + "mysqli_get_client_version" + "mysqli_get_connection_stats" + "mysqli_get_host_info" + "mysqli_get_links_stats" + "mysqli_get_proto_info" + "mysqli_get_server_info" + "mysqli_get_server_version" + "mysqli_get_warnings" + "mysqli_info" + "mysqli_init" + "mysqli_insert_id" + "mysqli_kill" + "mysqli_more_results" + "mysqli_multi_query" + "mysqli_next_result" + "mysqli_num_fields" + "mysqli_num_rows" + "mysqli_options" + "mysqli_ping" + "mysqli_poll" + "mysqli_prepare" + "mysqli_query" + "mysqli_real_connect" + "mysqli_real_escape_string" + "mysqli_real_query" + "mysqli_reap_async_query" + "mysqli_refresh" + "mysqli_release_savepoint" + "mysqli_report" + "mysqli_rollback" + "mysqli_savepoint" + "mysqli_select_db" + "mysqli_set_charset" + "mysqli_set_opt" + "mysqli_sqlstate" + "mysqli_ssl_set" + "mysqli_stat" + "mysqli_stmt_affected_rows" + "mysqli_stmt_attr_get" + "mysqli_stmt_attr_set" + "mysqli_stmt_bind_param" + "mysqli_stmt_bind_result" + "mysqli_stmt_close" + "mysqli_stmt_data_seek" + "mysqli_stmt_errno" + "mysqli_stmt_error" + "mysqli_stmt_error_list" + "mysqli_stmt_execute" + "mysqli_stmt_fetch" + "mysqli_stmt_field_count" + "mysqli_stmt_free_result" + "mysqli_stmt_get_result" + "mysqli_stmt_get_warnings" + "mysqli_stmt_init" + "mysqli_stmt_insert_id" + "mysqli_stmt_more_results" + "mysqli_stmt_next_result" + "mysqli_stmt_num_rows" + "mysqli_stmt_param_count" + "mysqli_stmt_prepare" + "mysqli_stmt_reset" + "mysqli_stmt_result_metadata" + "mysqli_stmt_send_long_data" + "mysqli_stmt_sqlstate" + "mysqli_stmt_store_result" + "mysqli_store_result" + "mysqli_thread_id" + "mysqli_thread_safe" + "mysqli_use_result" + "mysqli_warning_count") + (oauth + "oauth_get_sbs" + "oauth_urlencode") + (obsolete_7 + "__autoload" + "end" + "ezmlm_hash" + "is_real" + "jpeg2wbmp" + "mcrypt_create_iv" + "mcrypt_decrypt" + "mcrypt_enc_get_algorithms_name" + "mcrypt_enc_get_block_size" + "mcrypt_enc_get_iv_size" + "mcrypt_enc_get_key_size" + "mcrypt_enc_get_modes_name" + "mcrypt_enc_get_supported_key_sizes" + "mcrypt_enc_is_block_algorithm" + "mcrypt_enc_is_block_algorithm_mode" + "mcrypt_enc_is_block_mode" + "mcrypt_enc_self_test" + "mcrypt_encrypt" + "mcrypt_generic" + "mcrypt_generic_deinit" + "mcrypt_generic_init" + "mcrypt_get_block_size" + "mcrypt_get_cipher_name" + "mcrypt_get_iv_size" + "mcrypt_get_key_size" + "mcrypt_list_algorithms" + "mcrypt_list_modes" + "mcrypt_module_close" + "mcrypt_module_get_algo_block_size" + "mcrypt_module_get_algo_key_size" + "mcrypt_module_get_supported_key_sizes" + "mcrypt_module_is_block_algorithm" + "mcrypt_module_is_block_algorithm_mode" + "mcrypt_module_is_block_mode" + "mcrypt_module_open" + "mcrypt_module_self_test" + "mdecrypt_generic" + "png2wbmp" + "wddx_add_vars" + "wddx_deserialize" + "wddx_packet_end" + "wddx_packet_start" + "wddx_serialize_value" + "wddx_serialize_vars") + (obsolete_8 + "mhash" + "mhash_count" + "mhash_get_block_size" + "mhash_get_hash_name" + "mhash_keygen_s2k" + "utf8_decode" + "utf8_encode" + "zip_close" + "zip_entry_close" + "zip_entry_compressedsize" + "zip_entry_compressionmethod" + "zip_entry_filesize" + "zip_entry_name" + "zip_entry_open" + "zip_entry_read" + "zip_open" + "zip_read") + (oci8 + "oci_bind_array_by_name" + "oci_bind_by_name" + "oci_cancel" + "oci_client_version" + "oci_close" + "oci_commit" + "oci_connect" + "oci_define_by_name" + "oci_error" + "oci_execute" + "oci_fetch" + "oci_fetch_all" + "oci_fetch_array" + "oci_fetch_assoc" + "oci_fetch_object" + "oci_fetch_row" + "oci_field_is_null" + "oci_field_name" + "oci_field_precision" + "oci_field_scale" + "oci_field_size" + "oci_field_type" + "oci_field_type_raw" + "oci_free_descriptor" + "oci_free_statement" + "oci_get_implicit_resultset" + "oci_internal_debug" + "oci_lob_copy" + "oci_lob_is_equal" + "oci_new_collection" + "oci_new_connect" + "oci_new_cursor" + "oci_new_descriptor" + "oci_num_fields" + "oci_num_rows" + "oci_parse" + "oci_password_change" + "oci_pconnect" + "oci_register_taf_callback" + "oci_result" + "oci_rollback" + "oci_server_version" + "oci_set_action" + "oci_set_call_timeout" + "oci_set_client_identifier" + "oci_set_client_info" + "oci_set_db_operation" + "oci_set_edition" + "oci_set_module_name" + "oci_set_prefetch" + "oci_set_prefetch_lob" + "oci_statement_type" + "oci_unregister_taf_callback" + "ocibindbyname" + "ocicancel" + "ocicloselob" + "ocicollappend" + "ocicollassign" + "ocicollassignelem" + "ocicollgetelem" + "ocicollmax" + "ocicollsize" + "ocicolltrim" + "ocicolumnisnull" + "ocicolumnname" + "ocicolumnprecision" + "ocicolumnscale" + "ocicolumnsize" + "ocicolumntype" + "ocicolumntyperaw" + "ocicommit" + "ocidefinebyname" + "ocierror" + "ociexecute" + "ocifetch" + "ocifetchinto" + "ocifetchstatement" + "ocifreecollection" + "ocifreecursor" + "ocifreedesc" + "ocifreestatement" + "ociinternaldebug" + "ociloadlob" + "ocilogoff" + "ocilogon" + "ocinewcollection" + "ocinewcursor" + "ocinewdescriptor" + "ocinlogon" + "ocinumcols" + "ociparse" + "ociplogon" + "ociresult" + "ocirollback" + "ocirowcount" + "ocisavelob" + "ocisavelobfile" + "ociserverversion" + "ocisetprefetch" + "ocistatementtype" + "ociwritelobtofile" + "ociwritetemporarylob") + (odbc + "odbc_autocommit" + "odbc_binmode" + "odbc_close" + "odbc_close_all" + "odbc_columnprivileges" + "odbc_columns" + "odbc_commit" + "odbc_connect" + "odbc_cursor" + "odbc_data_source" + "odbc_do" + "odbc_error" + "odbc_errormsg" + "odbc_exec" + "odbc_execute" + "odbc_fetch_array" + "odbc_fetch_into" + "odbc_fetch_object" + "odbc_fetch_row" + "odbc_field_len" + "odbc_field_name" + "odbc_field_num" + "odbc_field_precision" + "odbc_field_scale" + "odbc_field_type" + "odbc_foreignkeys" + "odbc_free_result" + "odbc_gettypeinfo" + "odbc_longreadlen" + "odbc_next_result" + "odbc_num_fields" + "odbc_num_rows" + "odbc_pconnect" + "odbc_prepare" + "odbc_primarykeys" + "odbc_procedurecolumns" + "odbc_procedures" + "odbc_result" + "odbc_result_all" + "odbc_rollback" + "odbc_setoption" + "odbc_specialcolumns" + "odbc_statistics" + "odbc_tableprivileges" + "odbc_tables") + (openal + "openal_buffer_create" + "openal_buffer_data" + "openal_buffer_destroy" + "openal_buffer_get" + "openal_buffer_loadwav" + "openal_context_create" + "openal_context_current" + "openal_context_destroy" + "openal_context_process" + "openal_context_suspend" + "openal_device_close" + "openal_device_open" + "openal_listener_get" + "openal_listener_set" + "openal_source_create" + "openal_source_destroy" + "openal_source_get" + "openal_source_pause" + "openal_source_play" + "openal_source_rewind" + "openal_source_set" + "openal_source_stop" + "openal_stream") + (openssl + "openssl_cipher_iv_length" + "openssl_cms_decrypt" + "openssl_cms_encrypt" + "openssl_cms_read" + "openssl_cms_sign" + "openssl_cms_verify" + "openssl_csr_export" + "openssl_csr_export_to_file" + "openssl_csr_get_public_key" + "openssl_csr_get_subject" + "openssl_csr_new" + "openssl_csr_sign" + "openssl_decrypt" + "openssl_dh_compute_key" + "openssl_digest" + "openssl_encrypt" + "openssl_error_string" + "openssl_free_key" + "openssl_get_cert_locations" + "openssl_get_cipher_methods" + "openssl_get_curve_names" + "openssl_get_md_methods" + "openssl_get_privatekey" + "openssl_get_publickey" + "openssl_open" + "openssl_pbkdf2" + "openssl_pkcs12_export" + "openssl_pkcs12_export_to_file" + "openssl_pkcs12_read" + "openssl_pkcs7_decrypt" + "openssl_pkcs7_encrypt" + "openssl_pkcs7_read" + "openssl_pkcs7_sign" + "openssl_pkcs7_verify" + "openssl_pkey_derive" + "openssl_pkey_export" + "openssl_pkey_export_to_file" + "openssl_pkey_free" + "openssl_pkey_get_details" + "openssl_pkey_get_private" + "openssl_pkey_get_public" + "openssl_pkey_new" + "openssl_private_decrypt" + "openssl_private_encrypt" + "openssl_public_decrypt" + "openssl_public_encrypt" + "openssl_random_pseudo_bytes" + "openssl_seal" + "openssl_sign" + "openssl_spki_export" + "openssl_spki_export_challenge" + "openssl_spki_new" + "openssl_spki_verify" + "openssl_verify" + "openssl_x509_check_private_key" + "openssl_x509_checkpurpose" + "openssl_x509_export" + "openssl_x509_export_to_file" + "openssl_x509_fingerprint" + "openssl_x509_free" + "openssl_x509_parse" + "openssl_x509_read" + "openssl_x509_verify") + (parallel + "parallel\\bootstrap" + "parallel\\run") + (pcntl + "pcntl_alarm" + "pcntl_async_signals" + "pcntl_errno" + "pcntl_exec" + "pcntl_fork" + "pcntl_get_last_error" + "pcntl_getpriority" + "pcntl_rfork" + "pcntl_setpriority" + "pcntl_signal" + "pcntl_signal_dispatch" + "pcntl_signal_get_handler" + "pcntl_sigprocmask" + "pcntl_sigtimedwait" + "pcntl_sigwaitinfo" + "pcntl_strerror" + "pcntl_unshare" + "pcntl_wait" + "pcntl_waitpid" + "pcntl_wexitstatus" + "pcntl_wifexited" + "pcntl_wifsignaled" + "pcntl_wifstopped" + "pcntl_wstopsig" + "pcntl_wtermsig") + (pgsql + "pg_affected_rows" + "pg_cancel_query" + "pg_client_encoding" + "pg_close" + "pg_connect" + "pg_connect_poll" + "pg_connection_busy" + "pg_connection_reset" + "pg_connection_status" + "pg_consume_input" + "pg_convert" + "pg_copy_from" + "pg_copy_to" + "pg_dbname" + "pg_delete" + "pg_end_copy" + "pg_escape_bytea" + "pg_escape_identifier" + "pg_escape_literal" + "pg_escape_string" + "pg_execute" + "pg_fetch_all" + "pg_fetch_all_columns" + "pg_fetch_array" + "pg_fetch_assoc" + "pg_fetch_object" + "pg_fetch_result" + "pg_fetch_row" + "pg_field_is_null" + "pg_field_name" + "pg_field_num" + "pg_field_prtlen" + "pg_field_size" + "pg_field_table" + "pg_field_type" + "pg_field_type_oid" + "pg_flush" + "pg_free_result" + "pg_get_notify" + "pg_get_pid" + "pg_get_result" + "pg_host" + "pg_insert" + "pg_last_error" + "pg_last_notice" + "pg_last_oid" + "pg_lo_close" + "pg_lo_create" + "pg_lo_export" + "pg_lo_import" + "pg_lo_open" + "pg_lo_read" + "pg_lo_read_all" + "pg_lo_seek" + "pg_lo_tell" + "pg_lo_truncate" + "pg_lo_unlink" + "pg_lo_write" + "pg_meta_data" + "pg_num_fields" + "pg_num_rows" + "pg_options" + "pg_parameter_status" + "pg_pconnect" + "pg_ping" + "pg_port" + "pg_prepare" + "pg_put_line" + "pg_query" + "pg_query_params" + "pg_result_error" + "pg_result_error_field" + "pg_result_seek" + "pg_result_status" + "pg_select" + "pg_send_execute" + "pg_send_prepare" + "pg_send_query" + "pg_send_query_params" + "pg_set_client_encoding" + "pg_set_error_verbosity" + "pg_socket" + "pg_trace" + "pg_transaction_status" + "pg_tty" + "pg_unescape_bytea" + "pg_untrace" + "pg_update" + "pg_version") + (posix + "posix_access" + "posix_ctermid" + "posix_errno" + "posix_get_last_error" + "posix_getcwd" + "posix_getegid" + "posix_geteuid" + "posix_getgid" + "posix_getgrgid" + "posix_getgrnam" + "posix_getgroups" + "posix_getlogin" + "posix_getpgid" + "posix_getpgrp" + "posix_getpid" + "posix_getppid" + "posix_getpwnam" + "posix_getpwuid" + "posix_getrlimit" + "posix_getsid" + "posix_getuid" + "posix_initgroups" + "posix_isatty" + "posix_kill" + "posix_mkfifo" + "posix_mknod" + "posix_setegid" + "posix_seteuid" + "posix_setgid" + "posix_setpgid" + "posix_setrlimit" + "posix_setsid" + "posix_setuid" + "posix_strerror" + "posix_times" + "posix_ttyname" + "posix_uname") + (ps + "ps_add_bookmark" + "ps_add_launchlink" + "ps_add_locallink" + "ps_add_note" + "ps_add_pdflink" + "ps_add_weblink" + "ps_arc" + "ps_arcn" + "ps_begin_page" + "ps_begin_pattern" + "ps_begin_template" + "ps_circle" + "ps_clip" + "ps_close" + "ps_close_image" + "ps_closepath" + "ps_closepath_stroke" + "ps_continue_text" + "ps_curveto" + "ps_delete" + "ps_end_page" + "ps_end_pattern" + "ps_end_template" + "ps_fill" + "ps_fill_stroke" + "ps_findfont" + "ps_get_buffer" + "ps_get_parameter" + "ps_get_value" + "ps_hyphenate" + "ps_include_file" + "ps_lineto" + "ps_makespotcolor" + "ps_moveto" + "ps_new" + "ps_open_file" + "ps_open_image" + "ps_open_image_file" + "ps_open_memory_image" + "ps_place_image" + "ps_rect" + "ps_restore" + "ps_rotate" + "ps_save" + "ps_scale" + "ps_set_border_color" + "ps_set_border_dash" + "ps_set_border_style" + "ps_set_info" + "ps_set_parameter" + "ps_set_text_pos" + "ps_set_value" + "ps_setcolor" + "ps_setdash" + "ps_setflat" + "ps_setfont" + "ps_setgray" + "ps_setlinecap" + "ps_setlinejoin" + "ps_setlinewidth" + "ps_setmiterlimit" + "ps_setoverprintmode" + "ps_setpolydash" + "ps_shading" + "ps_shading_pattern" + "ps_shfill" + "ps_show" + "ps_show2" + "ps_show_boxed" + "ps_show_xy" + "ps_show_xy2" + "ps_string_geometry" + "ps_stringwidth" + "ps_stroke" + "ps_symbol" + "ps_symbol_name" + "ps_symbol_width" + "ps_translate") + (pspell + "pspell_add_to_personal" + "pspell_add_to_session" + "pspell_check" + "pspell_clear_session" + "pspell_config_create" + "pspell_config_data_dir" + "pspell_config_dict_dir" + "pspell_config_ignore" + "pspell_config_mode" + "pspell_config_personal" + "pspell_config_repl" + "pspell_config_runtogether" + "pspell_config_save_repl" + "pspell_new" + "pspell_new_config" + "pspell_new_personal" + "pspell_save_wordlist" + "pspell_store_replacement" + "pspell_suggest") + (radius + "radius_acct_open" + "radius_add_server" + "radius_auth_open" + "radius_close" + "radius_config" + "radius_create_request" + "radius_cvt_addr" + "radius_cvt_int" + "radius_cvt_string" + "radius_demangle" + "radius_demangle_mppe_key" + "radius_get_attr" + "radius_get_tagged_attr_data" + "radius_get_tagged_attr_tag" + "radius_get_vendor_attr" + "radius_put_addr" + "radius_put_attr" + "radius_put_int" + "radius_put_string" + "radius_put_vendor_addr" + "radius_put_vendor_attr" + "radius_put_vendor_int" + "radius_put_vendor_string" + "radius_request_authenticator" + "radius_salt_encrypt_attr" + "radius_send_request" + "radius_server_secret" + "radius_strerror") + (rar + "rar_broken_is" + "rar_close" + "rar_comment_get" + "rar_entry_get" + "rar_list" + "rar_open" + "rar_solid_is" + "rar_wrapper_cache_stats") + (readline + "readline" + "readline_add_history" + "readline_callback_handler_install" + "readline_callback_handler_remove" + "readline_callback_read_char" + "readline_clear_history" + "readline_completion_function" + "readline_info" + "readline_list_history" + "readline_on_new_line" + "readline_read_history" + "readline_redisplay" + "readline_write_history") + (recode + "recode" + "recode_file" + "recode_string") + (rpminfo + "rpmaddtag" + "rpmdbinfo" + "rpmdbsearch" + "rpminfo" + "rpmvercmp") + (rrd + "rrd_create" + "rrd_error" + "rrd_fetch" + "rrd_first" + "rrd_graph" + "rrd_info" + "rrd_last" + "rrd_lastupdate" + "rrd_restore" + "rrd_tune" + "rrd_update" + "rrd_version" + "rrd_xport" + "rrdc_disconnect") + (runkit7 + "runkit7_constant_add" + "runkit7_constant_redefine" + "runkit7_constant_remove" + "runkit7_function_add" + "runkit7_function_copy" + "runkit7_function_redefine" + "runkit7_function_remove" + "runkit7_function_rename" + "runkit7_import" + "runkit7_method_add" + "runkit7_method_copy" + "runkit7_method_redefine" + "runkit7_method_remove" + "runkit7_method_rename" + "runkit7_object_id" + "runkit7_superglobals" + "runkit7_zval_inspect") + (scoutapm + "scoutapm_get_calls" + "scoutapm_list_instrumented_functions") + (shmop + "shmop_close" + "shmop_delete" + "shmop_open" + "shmop_read" + "shmop_size" + "shmop_write") + (simplexml + "dom_import_simplexml" + "simplexml_import_dom" + "simplexml_load_file" + "simplexml_load_string") + (smnp + "snmp2_get" + "snmp2_getnext" + "snmp2_real_walk" + "snmp2_set" + "snmp2_walk" + "snmp3_get" + "snmp3_getnext" + "snmp3_real_walk" + "snmp3_set" + "snmp3_walk" + "snmp_get_quick_print" + "snmp_get_valueretrieval" + "snmp_read_mib" + "snmp_set_enum_print" + "snmp_set_oid_numeric_print" + "snmp_set_oid_output_format" + "snmp_set_quick_print" + "snmp_set_valueretrieval" + "snmpget" + "snmpgetnext" + "snmprealwalk" + "snmpset" + "snmpwalk" + "snmpwalkoid") + (soap + "is_soap_fault" + "use_soap_error_handler") + (socket + "socket_accept" + "socket_addrinfo_bind" + "socket_addrinfo_connect" + "socket_addrinfo_explain" + "socket_addrinfo_lookup" + "socket_bind" + "socket_clear_error" + "socket_close" + "socket_cmsg_space" + "socket_connect" + "socket_create" + "socket_create_listen" + "socket_create_pair" + "socket_export_stream" + "socket_get_option" + "socket_get_status" + "socket_getopt" + "socket_getpeername" + "socket_getsockname" + "socket_import_stream" + "socket_last_error" + "socket_listen" + "socket_read" + "socket_recv" + "socket_recvfrom" + "socket_recvmsg" + "socket_select" + "socket_send" + "socket_sendmsg" + "socket_sendto" + "socket_set_block" + "socket_set_blocking" + "socket_set_nonblock" + "socket_set_option" + "socket_set_timeout" + "socket_setopt" + "socket_shutdown" + "socket_strerror" + "socket_write" + "socket_wsaprotocol_info_export" + "socket_wsaprotocol_info_import" + "socket_wsaprotocol_info_release") + (sodium + "sodium_add" + "sodium_base642bin" + "sodium_bin2base64" + "sodium_bin2hex" + "sodium_compare" + "sodium_crypto_aead_aes256gcm_decrypt" + "sodium_crypto_aead_aes256gcm_encrypt" + "sodium_crypto_aead_aes256gcm_is_available" + "sodium_crypto_aead_aes256gcm_keygen" + "sodium_crypto_aead_chacha20poly1305_decrypt" + "sodium_crypto_aead_chacha20poly1305_encrypt" + "sodium_crypto_aead_chacha20poly1305_ietf_decrypt" + "sodium_crypto_aead_chacha20poly1305_ietf_encrypt" + "sodium_crypto_aead_chacha20poly1305_ietf_keygen" + "sodium_crypto_aead_chacha20poly1305_keygen" + "sodium_crypto_aead_xchacha20poly1305_ietf_decrypt" + "sodium_crypto_aead_xchacha20poly1305_ietf_encrypt" + "sodium_crypto_aead_xchacha20poly1305_ietf_keygen" + "sodium_crypto_auth" + "sodium_crypto_auth_keygen" + "sodium_crypto_auth_verify" + "sodium_crypto_box" + "sodium_crypto_box_keypair" + "sodium_crypto_box_keypair_from_secretkey_and_publickey" + "sodium_crypto_box_open" + "sodium_crypto_box_publickey" + "sodium_crypto_box_publickey_from_secretkey" + "sodium_crypto_box_seal" + "sodium_crypto_box_seal_open" + "sodium_crypto_box_secretkey" + "sodium_crypto_box_seed_keypair" + "sodium_crypto_core_ristretto255_add" + "sodium_crypto_core_ristretto255_from_hash" + "sodium_crypto_core_ristretto255_is_valid_point" + "sodium_crypto_core_ristretto255_random" + "sodium_crypto_core_ristretto255_scalar_add" + "sodium_crypto_core_ristretto255_scalar_complement" + "sodium_crypto_core_ristretto255_scalar_invert" + "sodium_crypto_core_ristretto255_scalar_mul" + "sodium_crypto_core_ristretto255_scalar_negate" + "sodium_crypto_core_ristretto255_scalar_random" + "sodium_crypto_core_ristretto255_scalar_reduce" + "sodium_crypto_core_ristretto255_scalar_sub" + "sodium_crypto_core_ristretto255_sub" + "sodium_crypto_generichash" + "sodium_crypto_generichash_final" + "sodium_crypto_generichash_init" + "sodium_crypto_generichash_keygen" + "sodium_crypto_generichash_update" + "sodium_crypto_kdf_derive_from_key" + "sodium_crypto_kdf_keygen" + "sodium_crypto_kx_client_session_keys" + "sodium_crypto_kx_keypair" + "sodium_crypto_kx_publickey" + "sodium_crypto_kx_secretkey" + "sodium_crypto_kx_seed_keypair" + "sodium_crypto_kx_server_session_keys" + "sodium_crypto_pwhash" + "sodium_crypto_pwhash_scryptsalsa208sha256" + "sodium_crypto_pwhash_scryptsalsa208sha256_str" + "sodium_crypto_pwhash_scryptsalsa208sha256_str_verify" + "sodium_crypto_pwhash_str" + "sodium_crypto_pwhash_str_needs_rehash" + "sodium_crypto_pwhash_str_verify" + "sodium_crypto_scalarmult" + "sodium_crypto_scalarmult_base" + "sodium_crypto_scalarmult_ristretto255" + "sodium_crypto_scalarmult_ristretto255_base" + "sodium_crypto_secretbox" + "sodium_crypto_secretbox_keygen" + "sodium_crypto_secretbox_open" + "sodium_crypto_secretstream_xchacha20poly1305_init_pull" + "sodium_crypto_secretstream_xchacha20poly1305_init_push" + "sodium_crypto_secretstream_xchacha20poly1305_keygen" + "sodium_crypto_secretstream_xchacha20poly1305_pull" + "sodium_crypto_secretstream_xchacha20poly1305_push" + "sodium_crypto_secretstream_xchacha20poly1305_rekey" + "sodium_crypto_shorthash" + "sodium_crypto_shorthash_keygen" + "sodium_crypto_sign" + "sodium_crypto_sign_detached" + "sodium_crypto_sign_ed25519_pk_to_curve25519" + "sodium_crypto_sign_ed25519_sk_to_curve25519" + "sodium_crypto_sign_keypair" + "sodium_crypto_sign_keypair_from_secretkey_and_publickey" + "sodium_crypto_sign_open" + "sodium_crypto_sign_publickey" + "sodium_crypto_sign_publickey_from_secretkey" + "sodium_crypto_sign_secretkey" + "sodium_crypto_sign_seed_keypair" + "sodium_crypto_sign_verify_detached" + "sodium_crypto_stream" + "sodium_crypto_stream_keygen" + "sodium_crypto_stream_xchacha20" + "sodium_crypto_stream_xchacha20_keygen" + "sodium_crypto_stream_xchacha20_xor" + "sodium_crypto_stream_xchacha20_xor_ic" + "sodium_crypto_stream_xor" + "sodium_hex2bin" + "sodium_increment" + "sodium_memcmp" + "sodium_memzero" + "sodium_pad" + "sodium_unpad") + (solr + "solr_get_version") + (sqlsrv + "sqlsrv_begin_transaction" + "sqlsrv_cancel" + "sqlsrv_client_info" + "sqlsrv_close" + "sqlsrv_commit" + "sqlsrv_configure" + "sqlsrv_connect" + "sqlsrv_errors" + "sqlsrv_execute" + "sqlsrv_fetch" + "sqlsrv_fetch_array" + "sqlsrv_fetch_object" + "sqlsrv_field_metadata" + "sqlsrv_free_stmt" + "sqlsrv_get_config" + "sqlsrv_get_field" + "sqlsrv_has_rows" + "sqlsrv_next_result" + "sqlsrv_num_fields" + "sqlsrv_num_rows" + "sqlsrv_prepare" + "sqlsrv_query" + "sqlsrv_rollback" + "sqlsrv_rows_affected" + "sqlsrv_send_stream_data" + "sqlsrv_server_info") + (ssdeep + "ssdeep_fuzzy_compare" + "ssdeep_fuzzy_hash" + "ssdeep_fuzzy_hash_filename") + (ssh2 + "ssh2_auth_agent" + "ssh2_auth_hostbased_file" + "ssh2_auth_none" + "ssh2_auth_password" + "ssh2_auth_pubkey_file" + "ssh2_connect" + "ssh2_disconnect" + "ssh2_exec" + "ssh2_fetch_stream" + "ssh2_fingerprint" + "ssh2_forward_accept" + "ssh2_forward_listen" + "ssh2_methods_negotiated" + "ssh2_poll" + "ssh2_publickey_add" + "ssh2_publickey_init" + "ssh2_publickey_list" + "ssh2_publickey_remove" + "ssh2_scp_recv" + "ssh2_scp_send" + "ssh2_send_eof" + "ssh2_sftp" + "ssh2_sftp_chmod" + "ssh2_sftp_lstat" + "ssh2_sftp_mkdir" + "ssh2_sftp_readlink" + "ssh2_sftp_realpath" + "ssh2_sftp_rename" + "ssh2_sftp_rmdir" + "ssh2_sftp_stat" + "ssh2_sftp_symlink" + "ssh2_sftp_unlink" + "ssh2_shell" + "ssh2_tunnel") + (stats + "stats_absolute_deviation" + "stats_cdf_beta" + "stats_cdf_binomial" + "stats_cdf_cauchy" + "stats_cdf_chisquare" + "stats_cdf_exponential" + "stats_cdf_f" + "stats_cdf_gamma" + "stats_cdf_laplace" + "stats_cdf_logistic" + "stats_cdf_negative_binomial" + "stats_cdf_noncentral_chisquare" + "stats_cdf_noncentral_f" + "stats_cdf_noncentral_t" + "stats_cdf_normal" + "stats_cdf_poisson" + "stats_cdf_t" + "stats_cdf_uniform" + "stats_cdf_weibull" + "stats_covariance" + "stats_dens_beta" + "stats_dens_cauchy" + "stats_dens_chisquare" + "stats_dens_exponential" + "stats_dens_f" + "stats_dens_gamma" + "stats_dens_laplace" + "stats_dens_logistic" + "stats_dens_normal" + "stats_dens_pmf_binomial" + "stats_dens_pmf_hypergeometric" + "stats_dens_pmf_negative_binomial" + "stats_dens_pmf_poisson" + "stats_dens_t" + "stats_dens_uniform" + "stats_dens_weibull" + "stats_harmonic_mean" + "stats_kurtosis" + "stats_rand_gen_beta" + "stats_rand_gen_chisquare" + "stats_rand_gen_exponential" + "stats_rand_gen_f" + "stats_rand_gen_funiform" + "stats_rand_gen_gamma" + "stats_rand_gen_ibinomial" + "stats_rand_gen_ibinomial_negative" + "stats_rand_gen_int" + "stats_rand_gen_ipoisson" + "stats_rand_gen_iuniform" + "stats_rand_gen_noncentral_chisquare" + "stats_rand_gen_noncentral_f" + "stats_rand_gen_noncentral_t" + "stats_rand_gen_normal" + "stats_rand_gen_t" + "stats_rand_get_seeds" + "stats_rand_phrase_to_seeds" + "stats_rand_ranf" + "stats_rand_setall" + "stats_skew" + "stats_standard_deviation" + "stats_stat_binomial_coef" + "stats_stat_correlation" + "stats_stat_factorial" + "stats_stat_independent_t" + "stats_stat_innerproduct" + "stats_stat_paired_t" + "stats_stat_percentile" + "stats_stat_powersum" + "stats_variance") + (stomp + "stomp_abort" + "stomp_ack" + "stomp_begin" + "stomp_close" + "stomp_commit" + "stomp_connect" + "stomp_connect_error" + "stomp_error" + "stomp_get_read_timeout" + "stomp_get_session_id" + "stomp_has_frame" + "stomp_read_frame" + "stomp_send" + "stomp_set_read_timeout" + "stomp_subscribe" + "stomp_unsubscribe" + "stomp_version") + (svn + "svn_add" + "svn_auth_get_parameter" + "svn_auth_set_parameter" + "svn_blame" + "svn_cat" + "svn_checkout" + "svn_cleanup" + "svn_client_version" + "svn_commit" + "svn_delete" + "svn_diff" + "svn_export" + "svn_fs_abort_txn" + "svn_fs_apply_text" + "svn_fs_begin_txn2" + "svn_fs_change_node_prop" + "svn_fs_check_path" + "svn_fs_contents_changed" + "svn_fs_copy" + "svn_fs_delete" + "svn_fs_dir_entries" + "svn_fs_file_contents" + "svn_fs_file_length" + "svn_fs_is_dir" + "svn_fs_is_file" + "svn_fs_make_dir" + "svn_fs_make_file" + "svn_fs_node_created_rev" + "svn_fs_node_prop" + "svn_fs_props_changed" + "svn_fs_revision_prop" + "svn_fs_revision_root" + "svn_fs_txn_root" + "svn_fs_youngest_rev" + "svn_import" + "svn_log" + "svn_ls" + "svn_mkdir" + "svn_repos_create" + "svn_repos_fs" + "svn_repos_fs_begin_txn_for_commit" + "svn_repos_fs_commit_txn" + "svn_repos_hotcopy" + "svn_repos_open" + "svn_repos_recover" + "svn_revert" + "svn_status" + "svn_update") + (swoole + "swoole_async_dns_lookup" + "swoole_async_read" + "swoole_async_readfile" + "swoole_async_set" + "swoole_async_write" + "swoole_async_writefile" + "swoole_clear_error" + "swoole_client_select" + "swoole_cpu_num" + "swoole_errno" + "swoole_error_log" + "swoole_event_add" + "swoole_event_defer" + "swoole_event_del" + "swoole_event_exit" + "swoole_event_set" + "swoole_event_wait" + "swoole_event_write" + "swoole_get_local_ip" + "swoole_last_error" + "swoole_load_module" + "swoole_select" + "swoole_set_process_name" + "swoole_strerror" + "swoole_timer_after" + "swoole_timer_clear" + "swoole_timer_exists" + "swoole_timer_tick" + "swoole_version") + (sysvshm + "ftok" + "msg_get_queue" + "msg_queue_exists" + "msg_receive" + "msg_remove_queue" + "msg_send" + "msg_set_queue" + "msg_stat_queue" + "sem_acquire" + "sem_get" + "sem_release" + "sem_remove" + "shm_attach" + "shm_detach" + "shm_get_var" + "shm_has_var" + "shm_put_var" + "shm_remove" + "shm_remove_var") + (taint + "is_tainted" + "taint" + "untaint") + (tcpwrap + "tcpwrap_check") + (tidy + "tidy_access_count" + "tidy_clean_repair" + "tidy_config_count" + "tidy_diagnose" + "tidy_error_count" + "tidy_get_body" + "tidy_get_config" + "tidy_get_error_buffer" + "tidy_get_head" + "tidy_get_html" + "tidy_get_html_ver" + "tidy_get_opt_doc" + "tidy_get_output" + "tidy_get_release" + "tidy_get_root" + "tidy_get_status" + "tidy_getopt" + "tidy_is_xhtml" + "tidy_is_xml" + "tidy_parse_file" + "tidy_parse_string" + "tidy_repair_file" + "tidy_repair_string" + "tidy_warning_count") + (trader + "trader_acos" + "trader_ad" + "trader_add" + "trader_adosc" + "trader_adx" + "trader_adxr" + "trader_apo" + "trader_aroon" + "trader_aroonosc" + "trader_asin" + "trader_atan" + "trader_atr" + "trader_avgprice" + "trader_bbands" + "trader_beta" + "trader_bop" + "trader_cci" + "trader_cdl2crows" + "trader_cdl3blackcrows" + "trader_cdl3inside" + "trader_cdl3linestrike" + "trader_cdl3outside" + "trader_cdl3starsinsouth" + "trader_cdl3whitesoldiers" + "trader_cdlabandonedbaby" + "trader_cdladvanceblock" + "trader_cdlbelthold" + "trader_cdlbreakaway" + "trader_cdlclosingmarubozu" + "trader_cdlconcealbabyswall" + "trader_cdlcounterattack" + "trader_cdldarkcloudcover" + "trader_cdldoji" + "trader_cdldojistar" + "trader_cdldragonflydoji" + "trader_cdlengulfing" + "trader_cdleveningdojistar" + "trader_cdleveningstar" + "trader_cdlgapsidesidewhite" + "trader_cdlgravestonedoji" + "trader_cdlhammer" + "trader_cdlhangingman" + "trader_cdlharami" + "trader_cdlharamicross" + "trader_cdlhighwave" + "trader_cdlhikkake" + "trader_cdlhikkakemod" + "trader_cdlhomingpigeon" + "trader_cdlidentical3crows" + "trader_cdlinneck" + "trader_cdlinvertedhammer" + "trader_cdlkicking" + "trader_cdlkickingbylength" + "trader_cdlladderbottom" + "trader_cdllongleggeddoji" + "trader_cdllongline" + "trader_cdlmarubozu" + "trader_cdlmatchinglow" + "trader_cdlmathold" + "trader_cdlmorningdojistar" + "trader_cdlmorningstar" + "trader_cdlonneck" + "trader_cdlpiercing" + "trader_cdlrickshawman" + "trader_cdlrisefall3methods" + "trader_cdlseparatinglines" + "trader_cdlshootingstar" + "trader_cdlshortline" + "trader_cdlspinningtop" + "trader_cdlstalledpattern" + "trader_cdlsticksandwich" + "trader_cdltakuri" + "trader_cdltasukigap" + "trader_cdlthrusting" + "trader_cdltristar" + "trader_cdlunique3river" + "trader_cdlupsidegap2crows" + "trader_cdlxsidegap3methods" + "trader_ceil" + "trader_cmo" + "trader_correl" + "trader_cos" + "trader_cosh" + "trader_dema" + "trader_div" + "trader_dx" + "trader_ema" + "trader_errno" + "trader_exp" + "trader_floor" + "trader_get_compat" + "trader_get_unstable_period" + "trader_ht_dcperiod" + "trader_ht_dcphase" + "trader_ht_phasor" + "trader_ht_sine" + "trader_ht_trendline" + "trader_ht_trendmode" + "trader_kama" + "trader_linearreg" + "trader_linearreg_angle" + "trader_linearreg_intercept" + "trader_linearreg_slope" + "trader_ln" + "trader_log10" + "trader_ma" + "trader_macd" + "trader_macdext" + "trader_macdfix" + "trader_mama" + "trader_mavp" + "trader_max" + "trader_maxindex" + "trader_medprice" + "trader_mfi" + "trader_midpoint" + "trader_midprice" + "trader_min" + "trader_minindex" + "trader_minmax" + "trader_minmaxindex" + "trader_minus_di" + "trader_minus_dm" + "trader_mom" + "trader_mult" + "trader_natr" + "trader_obv" + "trader_plus_di" + "trader_plus_dm" + "trader_ppo" + "trader_roc" + "trader_rocp" + "trader_rocr" + "trader_rocr100" + "trader_rsi" + "trader_sar" + "trader_sarext" + "trader_set_compat" + "trader_set_unstable_period" + "trader_sin" + "trader_sinh" + "trader_sma" + "trader_sqrt" + "trader_stddev" + "trader_stoch" + "trader_stochf" + "trader_stochrsi" + "trader_sub" + "trader_sum" + "trader_t3" + "trader_tan" + "trader_tanh" + "trader_tema" + "trader_trange" + "trader_trima" + "trader_trix" + "trader_tsf" + "trader_typprice" + "trader_ultosc" + "trader_var" + "trader_wclprice" + "trader_willr" + "trader_wma") + (ui + "UI\\Draw\\Text\\Font\\fontFamilies" + "UI\\quit" + "UI\\run") + (uopz + "uopz_add_function" + "uopz_allow_exit" + "uopz_backup" + "uopz_compose" + "uopz_copy" + "uopz_del_function" + "uopz_delete" + "uopz_extend" + "uopz_flags" + "uopz_function" + "uopz_get_exit_status" + "uopz_get_hook" + "uopz_get_mock" + "uopz_get_property" + "uopz_get_return" + "uopz_get_static" + "uopz_implement" + "uopz_overload" + "uopz_redefine" + "uopz_rename" + "uopz_restore" + "uopz_set_hook" + "uopz_set_mock" + "uopz_set_property" + "uopz_set_return" + "uopz_set_static" + "uopz_undefine" + "uopz_unset_hook" + "uopz_unset_mock" + "uopz_unset_return") + (var_representation + "var_representation") + (win32service + "win32_continue_service" + "win32_create_service" + "win32_delete_service" + "win32_get_last_control_message" + "win32_pause_service" + "win32_query_service_status" + "win32_send_custom_control" + "win32_set_service_exit_code" + "win32_set_service_exit_mode" + "win32_set_service_status" + "win32_start_service" + "win32_start_service_ctrl_dispatcher" + "win32_stop_service") + (wincache + "wincache_fcache_fileinfo" + "wincache_fcache_meminfo" + "wincache_lock" + "wincache_ocache_fileinfo" + "wincache_ocache_meminfo" + "wincache_refresh_if_changed" + "wincache_rplist_fileinfo" + "wincache_rplist_meminfo" + "wincache_scache_info" + "wincache_scache_meminfo" + "wincache_ucache_add" + "wincache_ucache_cas" + "wincache_ucache_clear" + "wincache_ucache_dec" + "wincache_ucache_delete" + "wincache_ucache_exists" + "wincache_ucache_get" + "wincache_ucache_inc" + "wincache_ucache_info" + "wincache_ucache_meminfo" + "wincache_ucache_set" + "wincache_unlock") + (xattr + "xattr_get" + "xattr_list" + "xattr_remove" + "xattr_set" + "xattr_supported") + (xdiff + "xdiff_file_bdiff" + "xdiff_file_bdiff_size" + "xdiff_file_bpatch" + "xdiff_file_diff" + "xdiff_file_diff_binary" + "xdiff_file_merge3" + "xdiff_file_patch" + "xdiff_file_patch_binary" + "xdiff_file_rabdiff" + "xdiff_string_bdiff" + "xdiff_string_bdiff_size" + "xdiff_string_bpatch" + "xdiff_string_diff" + "xdiff_string_diff_binary" + "xdiff_string_merge3" + "xdiff_string_patch" + "xdiff_string_patch_binary" + "xdiff_string_rabdiff") + (xhprof + "xhprof_disable" + "xhprof_enable" + "xhprof_sample_disable" + "xhprof_sample_enable") + (xml + "xml_error_string" + "xml_get_current_byte_index" + "xml_get_current_column_number" + "xml_get_current_line_number" + "xml_get_error_code" + "xml_parse" + "xml_parse_into_struct" + "xml_parser_create" + "xml_parser_create_ns" + "xml_parser_free" + "xml_parser_get_option" + "xml_parser_set_option" + "xml_set_character_data_handler" + "xml_set_default_handler" + "xml_set_element_handler" + "xml_set_end_namespace_decl_handler" + "xml_set_external_entity_ref_handler" + "xml_set_notation_decl_handler" + "xml_set_object" + "xml_set_processing_instruction_handler" + "xml_set_start_namespace_decl_handler" + "xml_set_unparsed_entity_decl_handler") + (xmlrpc + "xmlrpc_decode" + "xmlrpc_decode_request" + "xmlrpc_encode" + "xmlrpc_encode_request" + "xmlrpc_get_type" + "xmlrpc_is_fault" + "xmlrpc_parse_method_descriptions" + "xmlrpc_server_add_introspection_data" + "xmlrpc_server_call_method" + "xmlrpc_server_create" + "xmlrpc_server_destroy" + "xmlrpc_server_register_introspection_callback" + "xmlrpc_server_register_method" + "xmlrpc_set_type") + (xmlwriter + "xmlwriter_end_attribute" + "xmlwriter_end_cdata" + "xmlwriter_end_comment" + "xmlwriter_end_document" + "xmlwriter_end_dtd" + "xmlwriter_end_dtd_attlist" + "xmlwriter_end_dtd_element" + "xmlwriter_end_dtd_entity" + "xmlwriter_end_element" + "xmlwriter_end_pi" + "xmlwriter_flush" + "xmlwriter_full_end_element" + "xmlwriter_open_memory" + "xmlwriter_open_uri" + "xmlwriter_output_memory" + "xmlwriter_set_indent" + "xmlwriter_set_indent_string" + "xmlwriter_start_attribute" + "xmlwriter_start_attribute_ns" + "xmlwriter_start_cdata" + "xmlwriter_start_comment" + "xmlwriter_start_document" + "xmlwriter_start_dtd" + "xmlwriter_start_dtd_attlist" + "xmlwriter_start_dtd_element" + "xmlwriter_start_dtd_entity" + "xmlwriter_start_element" + "xmlwriter_start_element_ns" + "xmlwriter_start_pi" + "xmlwriter_text" + "xmlwriter_write_attribute" + "xmlwriter_write_attribute_ns" + "xmlwriter_write_cdata" + "xmlwriter_write_comment" + "xmlwriter_write_dtd" + "xmlwriter_write_dtd_attlist" + "xmlwriter_write_dtd_element" + "xmlwriter_write_dtd_entity" + "xmlwriter_write_element" + "xmlwriter_write_element_ns" + "xmlwriter_write_pi" + "xmlwriter_write_raw") + (yaml + "yaml_emit" + "yaml_emit_file" + "yaml_parse" + "yaml_parse_file" + "yaml_parse_url") + (yaz + "yaz_addinfo" + "yaz_ccl_conf" + "yaz_ccl_parse" + "yaz_close" + "yaz_connect" + "yaz_database" + "yaz_element" + "yaz_errno" + "yaz_error" + "yaz_es" + "yaz_es_result" + "yaz_get_option" + "yaz_hits" + "yaz_itemorder" + "yaz_present" + "yaz_range" + "yaz_record" + "yaz_scan" + "yaz_scan_result" + "yaz_schema" + "yaz_search" + "yaz_set_option" + "yaz_sort" + "yaz_syntax" + "yaz_wait") + (zlib + "deflate_add" + "deflate_init" + "gzclose" + "gzcompress" + "gzdecode" + "gzdeflate" + "gzencode" + "gzeof" + "gzfile" + "gzgetc" + "gzgets" + "gzgetss" + "gzinflate" + "gzopen" + "gzpassthru" + "gzputs" + "gzread" + "gzrewind" + "gzseek" + "gztell" + "gzuncompress" + "gzwrite" + "inflate_add" + "inflate_get_read_len" + "inflate_get_status" + "inflate_init" + "readgzfile" + "zlib_decode" + "zlib_encode" + "zlib_get_coding_type") + (zookeeper + "zookeeper_dispatch"))) + +(provide 'php-defs) +;;; php-defs.el ends here diff --git a/lisp/php-face.el b/lisp/php-face.el index ca7524c7..147fb813 100644 --- a/lisp/php-face.el +++ b/lisp/php-face.el @@ -1,10 +1,10 @@ ;;; php-face.el --- Face definitions for PHP script -*- lexical-binding: t; -*- -;; Copyright (C) 2022 Friends of Emacs-PHP development +;; Copyright (C) 2023 Friends of Emacs-PHP development ;; Author: USAMI Kenta ;; Created: 5 May 2019 -;; Version: 1.24.1 +;; Version: 1.26.1 ;; Keywords: faces, php ;; Homepage: https://github.com/emacs-php/php-mode ;; License: GPL-3.0-or-later @@ -55,20 +55,42 @@ :group 'php-faces :tag "PHP Function Name") -(defface php-function-call '((t ())) +(defface php-function-call-standard `((t ,(when (eval-when-compile (get 'font-lock-function-call-face 'face-defface-spec)) + '(:inherit font-lock-function-call-face)))) "PHP Mode face used to highlight function names in calles." :group 'php-faces - :tag "PHP Function Call") + :tag "PHP Function Call Standard") -(defface php-method-call '((t (:inherit php-function-call))) +(defface php-function-call-traditional '((t ())) + "PHP Mode face used to highlight function names in calles." + :group 'php-faces + :tag "PHP Function Call Traditional") + +(define-obsolete-face-alias 'php-function-call 'php-function-call-traditional "1.26.0") + +(defface php-method-call-standard '((t (:inherit php-function-call-standard))) + "PHP Mode face used to highlight method names in calles." + :group 'php-faces + :tag "PHP Method Call Standard") + +(defface php-method-call-traditional '((t (:inherit php-function-call-traditional))) "PHP Mode face used to highlight method names in calles." :group 'php-faces - :tag "PHP Method Call") + :tag "PHP Method Call Traditional") -(defface php-static-method-call '((t (:inherit php-method-call))) +(define-obsolete-face-alias 'php-method-call 'php-method-call-traditional "1.26.0") + +(defface php-static-method-call-standard '((t (:inherit php-method-call-standard))) + "PHP Mode face used to highlight static method names in calles." + :group 'php-faces + :tag "PHP Static Method Call Standard") + +(defface php-static-method-call-traditional '((t (:inherit php-method-call-traditional))) "PHP Mode face used to highlight static method names in calles." :group 'php-faces - :tag "PHP Static Method Call") + :tag "PHP Static Method Call Traditional") + +(define-obsolete-face-alias 'php-static-method-call 'php-static-method-call-traditional "1.26.0") (defface php-variable-name '((t (:inherit font-lock-variable-name-face))) "PHP Mode face used to highlight variable names." @@ -126,7 +148,8 @@ :tag "PHP Object Op") (defface php-paamayim-nekudotayim '((t ())) - "PHP Mode face used to highlight \"Paamayim Nekudotayim\" scope resolution operators (::)." + "PHP Mode face used to highlight scope resolution operators (::). +The operator is also knows as \"Paamayim Nekudotayim\"." :group 'php-faces :tag "PHP Paamayim Nekudotayim") @@ -145,7 +168,7 @@ :group 'php-faces :tag "PHP Constant") -(defface php-constant-assign '((t (:inherit font-lock-type-face))) +(defface php-constant-assign '((t (:inherit php-constant))) "PHP Mode face used to highlight constant assigning (\"const\" statement)." :group 'php-faces :tag "PHP Constant Assign") @@ -155,16 +178,19 @@ :group 'php-faces :tag "PHP Magical Constant") -(defface php-$this '((t (:inherit php-constant))) +(defface php-this '((t (:inherit php-constant))) "PHP Mode face used to highlight $this variables." :group 'php-faces :tag "PHP $this") -(defface php-$this-sigil '((t (:inherit php-constant))) +(defface php-this-sigil '((t (:inherit php-constant))) "PHP Mode face used to highlight sigils($) of $this variable." :group 'php-faces :tag "PHP $this Sigil") +(define-obsolete-face-alias 'php-$this 'php-this "1.26.0") +(define-obsolete-face-alias 'php-$this-sigil 'php-this-sigil "1.26.0") + (defface php-errorcontrol-op '((t (:inherit font-lock-type-face))) "PHP Mode face used to highlight errorcontrol operators (@).." :group 'php-faces @@ -175,7 +201,10 @@ :group 'php-faces :tag "PHP php Tag") -(defface php-doc-annotation-tag '((t . (:inherit font-lock-constant-face))) +(defface php-doc-annotation-tag (eval-when-compile + (if (eval-when-compile (boundp 'font-lock-doc-markup-face)) + '((t . (:inherit font-lock-doc-markup-face))) + '((t . (:inherit font-lock-constant-face))))) "Face used to highlight annotation tags in doc-comment." :group 'php-faces :tag "PHPDoc Annotation Tag") @@ -206,7 +235,8 @@ :tag "PHP Class Declaration") (defface php-class-declaration-spec '((t (:inherit php-keyword))) - "PHP Mode Face used to highlight class declaration specification keywords (implements, extends)." + "PHP Mode Face used to highlight class declaration specification keywords. +The keywords include: implements, extends." :group 'php-faces :tag "PHP Class Declaration Specification") @@ -236,7 +266,8 @@ :tag "PHP Visibility Modifier") (defface php-control-structure '((t (:inherit php-keyword))) - "PHP Mode Face used to highlight control structures (if, foreach, while, switch, catch...)." + "PHP Mode Face used to highlight control structures. +The control structures include: if, foreach, while, switch, catch." :group 'php-faces :tag "PHP Control Structure") diff --git a/lisp/php-flymake.el b/lisp/php-flymake.el new file mode 100644 index 00000000..8efad224 --- /dev/null +++ b/lisp/php-flymake.el @@ -0,0 +1,142 @@ +;;; php-flymake.el --- Flymake backend for PHP -*- lexical-binding: t; -*- + +;; Copyright (C) 2023 Friends of Emacs-PHP development + +;; Author: USAMI Kenta +;; Created: 5 Mar 2022 +;; Version: 1.26.1 +;; Keywords: tools, languages, php + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; Flymake backend for PHP. + +;;; Code: +(require 'flymake) +(require 'cl-lib) +(eval-and-compile + (require 'flymake-proc)) +(eval-when-compile + (require 'pcase) + (require 'rx)) + +(defgroup php-flymake nil + "Flymake backend for PHP." + :tag "PHP Flymake" + :group 'php) + +(defcustom php-flymake-executable-command-args nil + "List of command and arguments for `php -l'." + :group 'php-flymake + :type '(repeat string) + :safe (lambda (v) (and (listp v) (cl-every v #'stringp)))) + +(defconst php-flymake--diaggnostics-pattern + (eval-when-compile + (rx bol (? "PHP ") + (group (or "Parse" "Fatal")) ;; 1: type, not used + " error:" (+ (syntax whitespace)) + (group (+? any)) ;; 2: msg + " in " (group (+? any)) ;; 3: file, not used + " on line " (group (+ num)) ;; 4: line + eol))) + +(defvar-local php-flymake--proc nil) + +;;;###autoload +(defun php-flymake (report-fn &rest args) + "Flymake backend for PHP syntax check. + +See `flymake-diagnostic-functions' about REPORT-FN and ARGS parameters." + (setq-local flymake-proc-allowed-file-name-masks nil) + (when (process-live-p php-flymake--proc) + (if (plist-get args :interactive) + (user-error "There's already a Flymake process running in this buffer") + (kill-process php-flymake--proc))) + (save-restriction + (widen) + (cl-multiple-value-bind (use-stdin skip) (php-flymake--buffer-status) + (unless skip + (setq php-flymake--proc (php-flymake--make-process report-fn buffer-file-name (current-buffer) use-stdin)) + (when use-stdin + (process-send-region php-flymake--proc (point-min) (point-max))) + (process-send-eof php-flymake--proc))))) + +(defun php-flymake--buffer-status () + "Return buffer status about \"use STDIN\" and \"Skip diagnostic\"." + (let* ((use-stdin (or (null buffer-file-name) + (buffer-modified-p (current-buffer)) + (file-remote-p buffer-file-name))) + (skip (and (not use-stdin) + (save-excursion (goto-char (point-min)) (looking-at-p "#!"))))) + (cl-values use-stdin skip))) + +(defun php-flymake--diagnostics (locus source) + "Parse output of `php -l' command in SOURCE buffer. LOCUS means filename." + (unless (eval-when-compile (and (fboundp 'flymake-make-diagnostic) + (fboundp 'flymake-diag-region))) + (error "`php-flymake' requires Emacs 26.1 or later")) + (cl-loop + while (search-forward-regexp php-flymake--diaggnostics-pattern nil t) + for msg = (match-string 2) + for line = (string-to-number (match-string 4)) + for diag = (or (pcase-let ((`(,beg . ,end) + (flymake-diag-region source line))) + (flymake-make-diagnostic source beg end :error msg)) + (flymake-make-diagnostic locus (cons line nil) nil :error msg)) + return (list diag))) + +(defun php-flymake--build-command-line (file) + "Return the command line for `php -l' FILE." + (let* ((command-args (or php-flymake-executable-command-args + (list (or (bound-and-true-p php-executable) "php")))) + (cmd (car-safe command-args)) + (default-directory (expand-file-name "~"))) + (unless (or (file-executable-p cmd) + (executable-find cmd)) + (user-error "`%s' is not valid command" cmd)) + (nconc command-args + (list "-d" "display_errors=0") + (when file (list "-f" file)) + (list "-l")))) + +(defun php-flymake--make-process (report-fn locus source use-stdin) + "Make PHP process for syntax check SOURCE buffer. + +See `flymake-diagnostic-functions' about REPORT-FN parameter. +See `flymake-make-diagnostic' about LOCUS parameter." + (make-process + :name "php-flymake" + :buffer (generate-new-buffer "*flymake-php-flymake*") + :command (php-flymake--build-command-line (unless use-stdin locus)) + :noquery t :connection-type 'pipe + :sentinel + (lambda (p _ev) + (unwind-protect + (when (and (eq 'exit (process-status p)) + (with-current-buffer source (eq p php-flymake--proc))) + (with-current-buffer (process-buffer p) + (goto-char (point-min)) + (funcall report-fn + (if (zerop (process-exit-status p)) + nil + (php-flymake--diagnostics locus source))))) + (unless (process-live-p p) + ;; (display-buffer (process-buffer p)) ; uncomment to debug + (kill-buffer (process-buffer p))))))) + +(provide 'php-flymake) +;;; php-flymake.el ends here diff --git a/lisp/php-format.el b/lisp/php-format.el new file mode 100644 index 00000000..578ee52b --- /dev/null +++ b/lisp/php-format.el @@ -0,0 +1,236 @@ +;;; php-format.el --- Code reformatter for PHP buffer -*- lexical-binding: t; -*- + +;; Copyright (C) 2020 Friends of Emacs-PHP development + +;; Author: USAMI Kenta +;; Created: 5 Mar 2023 +;; Version: 0.1.0 +;; Keywords: tools, php +;; URL: https://github.com/emacs-php/php-mode.el +;; License: GPL-3.0-or-later + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; This feature is for execute PHP code formatting tools. + +;; ## Supported tools: +;; +;; - Easy Coding Standard (ecs) https://github.com/easy-coding-standard/easy-coding-standard +;; - PHP-CS-Fixer (php-cs-fixer) https://github.com/PHP-CS-Fixer/PHP-CS-Fixer +;; - PHP_CodeSniffer (phpcbf) https://github.com/squizlabs/PHP_CodeSniffer +;; +;; It supports both per-project and globally installed ones. +;; +;; ## How to use +;; +;; Add following line to setup function for php-mode. +;; +;; (php-format-auto-mode +1) +;; +;; ## Customization +;; +;; These variables can be set either by dir-locals.el or by custom-set-variable. +;; +;; - php-format-auto-mode-hook-depth +;; - php-format-command +;; - php-format-command-dir +;; - php-format-default-idle-time +;; - php-format-result-display-method-alist +;; +;; ## Display methods +;; +;; How formatting is performed and how the results are displayed can be controlled +;; by the following keywords. +;; +;; - idle: Asynchronously apply formatting to idle time in Emacs using `run-with-idle-timer' +;; - async: Immediately execute an asynchronous process to apply formatting +;; - compile: Apply formatting using the compile command. Doesn't lock, but results pop up +;; - silent: Apply formatting immediately and synchronously. +;; No message is displayed, but Emacs is locked while it is being processed. +;; - nil: Apply formatting immediately and synchronously. +;; Emacs will be locked until formatting is done and the result will pop up. +;; + +;;; Code: +(require 'cl-lib) +(require 'php-project) + +(defvar php-format-formatter-alist + '((ecs :marker ("ecs.php") + :command ("ecs" "check" "--fix" "--no-progress-bar" "--")) + (php-cs-fixer :marker (".php-cs-fixer.dist.php" ".php-cs-fixer.php") + :command ("php-cs-fixer" "fix" "--show-progress=none")) + (phpcbf :marker ("phpcs.xml.dist" "phpcs.xml") + :command ("phpcbf")))) + +(defvar php-format-lighter " phpf") +(defvar php-format-output-buffer " *PHP Format*") +(defvar php-format--exec-method nil) +(defvar php-format--idle-timer nil) + +;; Customize variables +(defgroup php-format nil + "Apply code reformat." + :tag "PHP Format" + :group 'php) + +(defcustom php-format-auto-mode-hook-depth -50 + "A depth number in the range -100..100 for `add-hook'." + :tag "PHP Format Auto Mode Hook Depth" + :type 'integer + :safe #'integerp + :group 'php) + +(defcustom php-format-command 'auto + "A formatter symbol, or a list of command and arguments." + :tag "PHP Format Command" + :type '(choice (const :tag "Disabled reformat codes" nil) + (const :tag "Auto" auto) + (const :tag "Easy Coding Standard" ecs) + (const :tag "PHP-CS-Fixer" php-cs-fixer) + (const :tag "PHP Code Beautifier and Fixer" phpcbf) + (repeat :tag "Command and arguments" string)) + :safe (lambda (v) (or (symbolp v) (listp v))) + :group 'php-format) + +(defcustom php-format-command-dir "vendor/bin" + "A relative path to the directory where formatting tool is installed." + :tag "PHP Format Command" + :type 'string + :safe #'stringp + :group 'php-format) + +(defcustom php-format-default-idle-time 3 + "Number of seconds to wait idle before formatting." + :tag "PHP Format Auto Mode Hook Depth" + :type 'integer + :safe #'integerp + :group 'php) + +(defcustom php-format-result-display-method-alist '((php-format-on-after-save-hook . idle) + (php-format-this-buffer-file . silent) + (php-format-project . compile)) + "An alist of misplay the result method of the formatting process." + :tag "PHP Format Result Display Method" + :type '(alist :key-type function + :value-type symbol) + :group 'php-format) + +(defcustom php-format-disable-async-format-buffer-has-modified t + "When set to non-NIL, disables async formatting if buffer is modified (unsaved)." + :tag "PHP Format Disable Async Format Buffer Has Modified" + :type 'boolean + :group 'php-format) + +;; Internal functions +(defsubst php-format--register-timer (sec command-args) + "Register idle-timer with SEC and COMMAND-ARGS." + (unless php-format--idle-timer + (setq php-format--idle-timer + (run-with-idle-timer sec nil #'php-format--execute-delayed-format + default-directory command-args)))) + +(defun php-format--execute-format (files) + "Execute PHP formatter with FILES." + (let* ((default-directory (php-project-get-root-dir)) + (command-args (php-format--get-command-args)) + command-line) + (when (null command-args) + (user-error "No available PHP formatter settings detected")) + (setq command-args (append command-args files)) + (setq command-line (mapconcat #'shell-quote-argument command-args " ")) + (pcase php-format--exec-method + (`(idle ,sec) (php-format--register-timer sec command-args)) + ('idle (php-format--register-timer php-format-default-idle-time command-args)) + ('async (apply #'call-process-shell-command (car command-args) nil nil nil + (append (cdr command-args) (list "&")))) + ('compile (compile command-line)) + ('silent (shell-command-to-string command-line)) + ('nil (shell-command command-line)) + (_ (user-error "`%s' is unexpected php-format--exec-method" php-format--exec-method))))) + +(defun php-format--get-command-args () + "Return a list of command and arguments." + (if (listp php-format-command) + php-format-command + (let ((cmd php-format-command) + args executable vendor) + (when (eq 'auto cmd) + (setq cmd (cl-loop for (sym . plist) in php-format-formatter-alist + for files = (plist-get plist :marker) + if (cl-find-if + (lambda (file) (file-exists-p (expand-file-name file default-directory))) + files) + return sym)) + (setq-local php-format-command cmd)) + (when-let* ((tup (plist-get (cdr-safe (assq cmd php-format-formatter-alist)) :command))) + (setq executable (car tup)) + (setq args (cdr tup)) + (setq vendor (expand-file-name executable (expand-file-name php-format-command-dir default-directory))) + (cond + ((file-exists-p vendor) (cons vendor args)) + ((executable-find executable) (cons executable args))))))) + +(defun php-format--execute-delayed-format (dir command-args) + "Asynchronously execute PHP format with COMMAND-ARGS in DIR." + (setq php-format--idle-timer nil) + (let ((default-directory dir)) + (when (not (and php-format-disable-async-format-buffer-has-modified + (buffer-modified-p))) + (apply #'call-process-shell-command (car command-args) nil nil nil + (append (cdr command-args) (list "&")))))) + +;; Public functions and minor mode + +;;;###autoload +(defun php-format-this-buffer-file () + "Apply format this buffer file." + (interactive) + (when php-format-command + (when (null buffer-file-name) + (user-error "This file has not yet been saved")) + (when (file-remote-p buffer-file-name) + (user-error "PHP Format feature does not yet support remote files")) + (let ((php-format--exec-method (cdr-safe (assq 'php-format-this-buffer-file php-format-result-display-method-alist)))) + (php-format--execute-format (list buffer-file-name))))) + +;;;###autoload +(defun php-format-project () + "Apply format this buffer file." + (interactive) + (unless php-format-command + (user-error "Disabled `php-format-command' in this project")) + (let ((php-format--exec-method (cdr-safe (assq 'php-format-project php-format-result-display-method-alist)))) + (php-format--execute-format nil))) + +;;;###autoload +(defun php-format-on-after-save-hook () + "Apply format on after save hook." + (when (and php-format-command buffer-file-name (not (file-remote-p buffer-file-name))) + (let ((php-format--exec-method (cdr-safe (assq 'php-format-on-after-save-hook php-format-result-display-method-alist)))) + (php-format--execute-format nil)))) + +;;;###autoload +(define-minor-mode php-format-auto-mode + "Automatically apply formatting when saving an edited file." + :group 'php-format + :lighter php-format-lighter + (if php-format-auto-mode + (add-hook 'after-save-hook 'php-format-on-after-save-hook php-format-auto-mode-hook-depth t) + (remove-hook 'after-save-hook 'php-format-on-after-save-hook t))) + +(provide 'php-format) +;;; php-format.el ends here diff --git a/lisp/php-ide-phpactor.el b/lisp/php-ide-phpactor.el new file mode 100644 index 00000000..109cd461 --- /dev/null +++ b/lisp/php-ide-phpactor.el @@ -0,0 +1,127 @@ +;;; php-ide-phpactor.el --- PHP-IDE feature using Phpactor RPC -*- lexical-binding: t; -*- + +;; Copyright (C) 2023 Friends of Emacs-PHP development + +;; Author: USAMI Kenta +;; Keywords: tools, files +;; URL: https://github.com/emacs-php/php-mode +;; Version: 1.26.1 +;; License: GPL-3.0-or-later + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; PHP-IDE implementation to integrate Phpactor (phpactor.el). +;; This feature depends on . + +;;; Code: +(require 'phpactor nil t) +(require 'popup nil t) +(require 'smart-jump nil t) +(require 'cl-lib) + +(defvar-local php-ide-phpactor-buffer nil) +(defvar-local php-ide-phpactor-hover-last-pos nil) +(defvar-local php-ide-phpactor-hover-last-msg nil) + +(declare-function phpactor--command-argments "ext:phpactor" (&rest arg-keys)) +(declare-function phpactor--parse-json "ext:phpactor" (buffer)) +(declare-function phpactor--rpc-async "ext:phpactor" (action arguments callback)) +(declare-function phpactor-goto-definition "ext:phpactor" ()) +(declare-function popup-tip "ext:popup" (string)) +(declare-function smart-jump-back "ext:smart-jump" ()) +(declare-function smart-jump-go "ext:smart-jump" (&optional smart-list continue)) +(declare-function smart-jump-references "ext:smart-jump" (&optional smart-list continue)) + +(defgroup php-ide-phpactor nil + "UI support for PHP developing." + :tag "PHP-IDE Phpactor" + :prefix "php-ide-phpactor-" + :group 'php-ide) + +(defcustom php-ide-phpactor-activate-features '(all) + "A set of Phpactor features you want to enable." + :tag "PHP-IDE Phpactor Activate Features" + :type '(set (const :tag "All" all) + (const hover) + (const navigation)) + :safe (lambda (xs) (and (listp xs) + (cl-every (lambda (x) (memq x '(all hover navigation))) xs))) + :group 'php-ide-phpactor) + +(defvar php-ide-phpactor-timer nil + "Timer object for execute Phpactor and display hover message.") + +(defvar php-ide-phpactor-disable-hover-at-point-functions + '(php-in-string-or-comment-p)) + +(defun php-ide-phpactor--disable-hover-at-point-p () + "Return non-NIL if any function return non-NIL for disable to hover at point." + (cl-loop for f in php-ide-phpactor-disable-hover-at-point-functions + never (not (funcall f)))) + +(defun php-ide-phpactor-hover () + "Show brief information about the symbol underneath the cursor." + (interactive) + (when (and php-ide-phpactor-buffer (not (php-ide-phpactor--disable-hover-at-point-p))) + (if (eq (point) php-ide-phpactor-hover-last-pos) + (when php-ide-phpactor-hover-last-msg + (let ((msg php-ide-phpactor-hover-last-msg)) + (setq php-ide-phpactor-hover-last-msg nil) + (popup-tip msg))) + (setq php-ide-phpactor-hover-last-pos (point)) + (let ((main-buffer (current-buffer))) + (phpactor--rpc-async "hover" (phpactor--command-argments :source :offset) + (lambda (proc) + (let* ((response (phpactor--parse-json (process-buffer proc))) + (msg (plist-get (plist-get response :parameters) :message))) + (with-current-buffer main-buffer + (setq php-ide-phpactor-hover-last-msg msg))))))))) + +(defsubst php-ide-phpactor--feature-activated-p (feature) + "Is FEATURE activated in `php-ide-phpactor-activate-features'." + (or (memq 'all php-ide-phpactor-activate-features) + (memq feature php-ide-phpactor-activate-features))) + +;;;###autoload +(defun php-ide-phpactor-activate () + "Activate PHP-IDE using phpactor.el." + (interactive) + (when (php-ide-phpactor--feature-activated-p 'navigation) + (if (not (bound-and-true-p phpactor-smart-jump-initialized)) + (local-set-key [remap xref-find-definitions] #'phpactor-goto-definition) + (local-set-key [remap xref-find-definitions] #'smart-jump-go) + (local-set-key [remap xref-pop-marker-stack] #'smart-jump-back) + (local-set-key [remap xref-find-references] #'smart-jump-references))) + (when (php-ide-phpactor--feature-activated-p 'hover) + (unless php-ide-phpactor-timer + (setq php-ide-phpactor-timer (run-with-timer 0.8 0.8 #'php-ide-phpactor-hover)))) + (setq php-ide-phpactor-buffer t)) + +;;;###autoload +(defun php-ide-phpactor-deactivate () + "Dectivate PHP-IDE using phpactor.el." + (interactive) + (local-unset-key [remap xref-find-definitions]) + (local-unset-key [remap xref-pop-marker-stack]) + (local-unset-key [remap xref-find-references]) + + (when php-ide-phpactor-timer + (cancel-timer php-ide-phpactor-timer) + (setq php-ide-phpactor-timer nil)) + (setq php-ide-phpactor-buffer nil)) + +(provide 'php-ide-phpactor) +;;; php-ide-phpactor.el ends here diff --git a/lisp/php-ide.el b/lisp/php-ide.el new file mode 100644 index 00000000..4e1ec8b4 --- /dev/null +++ b/lisp/php-ide.el @@ -0,0 +1,239 @@ +;;; php-ide.el --- IDE-like UI support for PHP development -*- lexical-binding: t; -*- + +;; Copyright (C) 2023 Friends of Emacs-PHP development + +;; Author: USAMI Kenta +;; Keywords: tools, files +;; URL: https://github.com/emacs-php/php-mode +;; Version: 1.26.1 +;; License: GPL-3.0-or-later + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; PHP Mode integrates LSP Mode (lsp-mode), Phpactor (phpactor.el) and IDE-like tools. +;; +;; **Note**: +;; This feature is under development and experimental. +;; All of these functions, modes and terms are subject to change without notice. +;; +;; ## Motivations +;; +;; There are some IDE-like features / packages for PHP development. +;; PHP-IDE bridges projects and their IDE-like features. +;; +;; ## IDE Features +;; +;; We don't recommend features, but bundle some feature bridges. +;; They are sorted alphabetically except "none." +;; +;; - none +;; Does not launch any IDE features. +;; - eglot +;; https://github.com/joaotavora/eglot +;; - lsp-bridge +;; https://github.com/manateelazycat/lsp-bridge +;; - lsp-mode +;; https://emacs-lsp.github.io/lsp-mode/ +;; https://github.com/emacs-lsp/lsp-mode +;; - phpactor +;; https://phpactor.readthedocs.io/ +;; https://github.com/phpactor/phpactor +;; https://github.com/emacs-php/phpactor.el +;; +;; ## Configuration +;; +;; Put follows code into your .emacs (~/.emacs.d/init.el) file: +;; +;; (defun init-php-mode-setup () +;; (add-hook 'hack-local-variables-hook #'php-ide-mode t t)) +;; +;; (defun init-php-ide-mode-setup (feature activate) +;; (pcase feature +;; (`lsp-bridge +;; (if activate +;; (progn (yas-minor-mode +1) +;; (corfu-mode -1)) +;; (yas-minor-mode -1) +;; (corfu-mode +1))))) +;; +;; (with-eval-after-load 'php-ide +;; (custom-set-variables +;; '(php-ide-features . 'eglot) ;; and/or 'none, 'phpactor, 'lsp-mode +;; '(php-ide-eglot-executable "psalm-language-server") ;; or "intelephense", '("php" "vendor/bin/path/to/server") +;; ;; If you want to hide php-ide-mode from the mode line, set an empty string +;; '(php-ide-mode-lighter "")) +;; +;; (add-hook 'php-mode-hook #'init-php-mode-setup) +;; (add-hook 'php-ide-mode-functions #'init-php-ide-mode-setup)) +;; +;; If you don't enable IDE support by default, set '(php-ide-feature 'none) +;; +;; ### For per project configuration +;; +;; Put follows code into .dir-locals.el in project directory: +;; +;; ((nil (php-project-root . git) +;; (php-ide-features . (lsp-mode)))) +;; +;; If you can't put .dir-locals.el in your project directory, consider the sidecar-locals package. +;; https://melpa.org/#/sidecar-locals +;; https://codeberg.org/ideasman42/emacs-sidecar-locals +;; + +;;; Code: +(require 'cl-lib) +(require 'php-project) + +(eval-when-compile + (require 'php-ide-phpactor) + (defvar eglot-server-programs) + (declare-function lsp-bridge-mode "ext:lsp-bridge" ()) + (declare-function eglot-ensure "ext:eglot" ()) + (declare-function eglot--managed-mode-off "ext:eglot" ()) + (declare-function phpactor--find-executable "ext:phpactor" ())) + +(defvar php-ide-feature-alist + '((none :test (lambda () t) + :activate (lambda () t) + :deactivate (lambda () t)) + (phpactor :test (lambda () (and (require 'phpactor nil t) (featurep 'phpactor))) + :activate php-ide-phpactor-activate + :deactivate php-ide-phpactor-activate) + (eglot :test (lambda () (and (require 'eglot nil t) (featurep 'eglot))) + :activate eglot-ensure + :deactivate eglot--managed-mode-off) + (lsp-bridge :test (lambda () (and (require 'lsp-bridge nil t) (featurep 'lsp-bridge))) + :activate (lambda () (lsp-bridge-mode +1)) + :deactivate (lambda () (lsp-bridge-mode -1))) + (lsp-mode :test (lambda () (and (require 'lsp nil t) (featurep 'lsp))) + :activate lsp + :deactivate lsp-workspace-shutdown))) + +(defvar php-ide-lsp-command-alist + '((intelephense "intelephense" "--stdio") + (phpactor . (lambda () (list (if (fboundp 'phpactor--find-executable) + (phpactor--find-executable) + "phpactor") + "language-server"))))) + +(defgroup php-ide nil + "IDE-like support for PHP developing." + :tag "PHP-IDE" + :prefix "php-ide-" + :group 'php) + +;;;###autoload +(defcustom php-ide-features nil + "A set of PHP-IDE features symbol." + :tag "PHP-IDE Feature" + :group 'php-ide + :type `(set ,@(mapcar (lambda (feature) (list 'const (car feature))) + php-ide-feature-alist) + symbol) + :safe (lambda (v) (cl-loop for feature in (if (listp v) v (list v)) + always (symbolp feature)))) + +;;;###autoload +(defcustom php-ide-eglot-executable nil + "Command name or path to the command of Eglot LSP executable." + :tag "PHP-IDE Eglot Executable" + :group 'php-ide + :type '(choice + (const intelephense) + (const phpactor) + string (repeat string)) + :safe (lambda (v) (cond + ((stringp v) (file-exists-p v)) + ((listp v) (cl-every #'stringp v)) + ((assq v php-ide-lsp-command-alist))))) + +;;;###autoload +(defun php-ide-eglot-server-program () + "Return a list of command to execute LSP Server." + (cond + ((stringp php-ide-eglot-executable) (list php-ide-eglot-executable)) + ((listp php-ide-eglot-executable) php-ide-eglot-executable) + ((when-let* ((command (assq php-ide-eglot-executable php-ide-lsp-command-alist))) + (cond + ((functionp command) (funcall command)) + ((listp command) command)))))) + +(defcustom php-ide-mode-lighter " PHP-IDE" + "A symbol of PHP-IDE feature." + :tag "PHP-IDE Mode Lighter" + :group 'php-ide + :type 'string + :safe #'stringp) + +;;;###autoload +(defcustom php-ide-mode-functions nil + "Hook functions called when before activating or deactivating PHP-IDE. +Notice that two arguments (FEATURE ACTIVATE) are given. + +FEATURE: A symbol, like \\='lsp-mode. +ACTIVATE: T is given when activeting, NIL when deactivating PHP-IDE." + :tag "PHP-IDE Mode Functions" + :group 'php-ide + :type '(repeat function) + :safe (lambda (functions) + (and (listp functions) (cl-every #'functionp functions)))) + +;;;###autoload +(define-minor-mode php-ide-mode + "Minor mode for integrate IDE-like tools." + :lighter php-ide-mode-lighter + (let ((ide-features php-ide-features)) + (when-let* ((unavailable-features (cl-loop for feature in ide-features + unless (assq feature php-ide-feature-alist) + collect feature))) + (user-error "%s includes unavailable PHP-IDE features. (available features are: %s)" + ide-features + (mapconcat (lambda (feature) (concat "'" (symbol-name feature))) + (php-ide--avilable-features) ", "))) + (cl-loop for feature in ide-features + for ide-plist = (cdr-safe (assq feature php-ide-feature-alist)) + do (if (null ide-plist) + (message "Please set `php-ide-feature' variable in .dir-locals.el or custom variable") + (run-hook-with-args 'php-ide-mode-functions feature php-ide-mode) + (if php-ide-mode + (php-ide--activate-buffer feature ide-plist) + (php-ide--deactivate-buffer ide-plist)))))) + +;;;###autoload +(defun php-ide-turn-on () + "Turn on PHP IDE-FEATURES and execute `php-ide-mode'." + (unless php-ide-features + (user-error "No PHP-IDE feature is installed. Install the lsp-mode, lsp-bridge, eglot or phpactor package")) + (php-ide-mode +1)) + +(defun php-ide--activate-buffer (name ide-plist) + "Activate php-ide implementation by NAME and IDE-PLIST." + (unless (funcall (plist-get ide-plist :test)) + (user-error "PHP-IDE feature `%s' is not available" name)) + (funcall (plist-get ide-plist :activate))) + +(defun php-ide--deactivate-buffer (ide-plist) + "Deactivate php-ide implementation by IDE-PLIST." + (funcall (plist-get ide-plist :deactivate))) + +(defun php-ide--avilable-features () + "Return list of available PHP-IDE features." + (cl-loop for (ide . plist) in php-ide-feature-alist + if (funcall (plist-get plist :test)) + collect ide)) + +(provide 'php-ide) +;;; php-ide.el ends here diff --git a/lisp/php-local-manual.el b/lisp/php-local-manual.el index dda26c5d..eec5cc3a 100644 --- a/lisp/php-local-manual.el +++ b/lisp/php-local-manual.el @@ -1,6 +1,6 @@ ;;; php-local-manual.el --- Tools for local PHP manual -*- lexical-binding: t; -*- -;; Copyright (C) 2022 Friends of Emacs-PHP development +;; Copyright (C) 2023 Friends of Emacs-PHP development ;; Author: phil-s ;; Maintainer: USAMI Kenta @@ -194,6 +194,7 @@ current `tags-file-name'." (setq php-local-manual--completion-table php-table)))) (defun php-local-manual-build-table-from-file (filename) + "Build a table of PHP function names from FILENAME." (let ((table (make-vector 1022 0)) (buf (find-file-noselect filename))) (with-current-buffer buf @@ -208,10 +209,11 @@ current `tags-file-name'." (defun php-local-manual-build-table-from-path (path) "Return list of PHP function name from `PATH' directory." - (cl-loop for file in (directory-files path nil "^function\\..+\\.html$") - if (string-match "\\.\\([-a-zA-Z_0-9]+\\)\\.html$" file) - collect (replace-regexp-in-string - "-" "_" (substring file (match-beginning 1) (match-end 1)) t))) + (save-match-data + (cl-loop for file in (directory-files path nil "^function\\..+\\.html$") + if (string-match "\\.\\([-a-zA-Z_0-9]+\\)\\.html$" file) + collect (replace-regexp-in-string + "-" "_" (substring file (match-beginning 1) (match-end 1)) t)))) (provide 'php-local-manual) ;;; php-local-manual.el ends here diff --git a/lisp/php-mode-debug.el b/lisp/php-mode-debug.el index d32485a1..efe6e48e 100644 --- a/lisp/php-mode-debug.el +++ b/lisp/php-mode-debug.el @@ -1,11 +1,11 @@ ;;; php-mode-debug.el --- Debug functions for PHP Mode -*- lexical-binding: t; -*- -;; Copyright (C) 2022 Friends of Emacs-PHP development +;; Copyright (C) 2023 Friends of Emacs-PHP development ;; Author: USAMI Kenta ;; URL: https://github.com/emacs-php/php-mode ;; Keywords: maint -;; Version: 1.24.1 +;; Version: 1.26.1 ;; License: GPL-3.0-or-later ;; This program is free software; you can redistribute it and/or modify @@ -31,8 +31,51 @@ (require 'php-mode) (require 'package) (require 'pkg-info nil t) +(require 'el-get nil t) -(declare-function pkg-info-version-info "pkg-info" (library &optional package show)) +(declare-function pkg-info-version-info "ext:pkg-info" (library &optional package show)) + +(defun php-mode-debug-reinstall (force &optional called-interactive) + "Reinstall PHP Mode to solve Cc Mode version mismatch. + +When FORCE, try to reinstall without interactively asking. +When CALLED-INTERACTIVE then message the result." + (interactive (list (yes-or-no-p (if (string= php-mode-cc-version c-version) + "No need to recompile, but force PHP Mode to reinstall? " + "Force reinstall PHP Mode? ")) + t)) + (let* ((cc-version-mismatched (string= php-mode-cc-version c-version)) + (preface (if cc-version-mismatched + "" + "CC Mode has been updated. "))) + (if (catch 'success + (cond + ((and (not called-interactive) + (not force) + cc-version-mismatched) + nil) + ((and (package-installed-p 'php-mode) + (or force + (yes-or-no-p (format "%sReinstall `php-mode' package? " preface)))) + (package-reinstall 'php-mode) + (throw 'success t)) + ;; This clause is not included in the byte-compiled code when compiled without El-Get + ((and (eval-when-compile (and (fboundp 'el-get-package-is-installed) + (fboundp 'el-get-reinstall))) + (el-get-package-is-installed 'php-mode) + (or force + (yes-or-no-p (format "%sReinstall `php-mode' package by El-Get? " preface)))) + (el-get-reinstall 'php-mode) + (throw 'success t)) + ((not called-interactive) + (user-error + (if cc-version-mismatched + "PHP Mode cannot be reinstalled automatically. Please try manually if necessary" + "Please reinstall or byte recompile PHP Mode files manually"))))) + (user-error "PHP Mode reinstalled successfully. Please restart Emacs") + (prog1 t + (when called-interactive + (message "PHP Mode was not reinstalled")))))) (defun php-mode-debug--buffer (&optional command &rest args) "Return buffer for php-mode-debug, and execute `COMMAND' with `ARGS'." @@ -62,7 +105,12 @@ (php-mode-debug--message "Pasting the following information on the issue will help us to investigate the cause.") (php-mode-debug--message "```") (php-mode-debug--message "--- PHP-MODE DEBUG BEGIN ---") - (php-mode-debug--message "versions: %s; %s; Cc Mode %s)" (emacs-version) (php-mode-version) c-version) + (php-mode-debug--message "versions: %s; %s; Cc Mode %s)" + (emacs-version) + (php-mode-version) + (if (string= php-mode-cc-version c-version) + c-version + (format "%s (php-mode-cc-version: %s *mismatched*)" c-version php-mode-cc-version))) (php-mode-debug--message "package-version: %s" (if (fboundp 'pkg-info) (pkg-info-version-info 'php-mode) diff --git a/lisp/php-mode.el b/lisp/php-mode.el index ecf80dd8..afd51294 100644 --- a/lisp/php-mode.el +++ b/lisp/php-mode.el @@ -1,6 +1,6 @@ ;;; php-mode.el --- Major mode for editing PHP code -*- lexical-binding: t; -*- -;; Copyright (C) 2022 Friends of Emacs-PHP development +;; Copyright (C) 2024 Friends of Emacs-PHP development ;; Copyright (C) 1999, 2000, 2001, 2003, 2004 Turadg Aleahmad ;; 2008 Aaron S. Hawley ;; 2011, 2012, 2013, 2014, 2015, 2016, 2017 Eric James Michael Ritz @@ -9,12 +9,14 @@ ;; Maintainer: USAMI Kenta ;; URL: https://github.com/emacs-php/php-mode ;; Keywords: languages php -;; Version: 1.24.1 -;; Package-Requires: ((emacs "25.2")) +;; Version: 1.26.1 +;; Package-Requires: ((emacs "27.1")) ;; License: GPL-3.0-or-later -(defconst php-mode-version-number "1.24.1" - "PHP Mode version number.") +(eval-and-compile + (make-obsolete-variable + (defconst php-mode-version-number "1.26.1" "PHP Mode version number.") + "Please call (php-mode-version :as-number t) for compatibility." "1.24.2")) ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by @@ -71,6 +73,7 @@ (require 'custom) (require 'speedbar) (require 'imenu) +(require 'consult-imenu nil t) (require 'package) (require 'nadvice) (require 'mode-local) @@ -79,14 +82,48 @@ (eval-when-compile (require 'rx) (require 'cl-lib) + (require 'flymake) + (require 'flymake-proc) + (require 'php-flymake) (require 'regexp-opt) + (declare-function acm-backend-tabnine-candidate-expand "ext:acm-backend-tabnine" + (candidate-info bound-start)) (defvar add-log-current-defun-header-regexp) (defvar add-log-current-defun-function) + (defvar c-syntactic-context) (defvar c-vsemi-status-unknown-p) (defvar syntax-propertize-via-font-lock)) +(defconst php-mode-version-id + (eval-when-compile + (let ((fallback-version (format "%s-non-vcs" (with-no-warnings php-mode-version-number)))) + (if (locate-dominating-file default-directory ".git") + (save-match-data + (let ((tag (replace-regexp-in-string + (rx bos "v") "" + (shell-command-to-string "git describe --tags"))) + (pattern (rx (group (+ any)) eol))) + (if (string-match pattern tag) + (match-string 0 tag) + (error "Faild to obtain git tag")))) + fallback-version))) + "PHP Mode build ID. + +The format is follows: + +\"1.23.4\": Tagged revision, compiled under Git VCS. +\"1.23.4-56-xxxxxx\": 56 commits after the last tag release, compiled under Git. +\"1.23.4-non-vcs\": Compiled in an environment not managed by Git VCS.") + (autoload 'php-mode-debug "php-mode-debug" "Display informations useful for debugging PHP Mode." t) + +(autoload 'php-mode-debug-reinstall "php-mode-debug" + "Reinstall PHP Mode to solve Cc Mode version mismatch. + +When FORCE, try to reinstall without interactively asking. +When CALLED-INTERACTIVE then message the result." t) + ;; Local variables @@ -103,7 +140,6 @@ (define-obsolete-variable-alias 'php-default-face 'php-mode-default-face "1.20.0") (defcustom php-mode-default-face 'default "Default face in `php-mode' buffers." - :group 'php-mode :tag "PHP Mode Default Face" :type 'face) @@ -111,7 +147,6 @@ (defcustom php-mode-speedbar-config t "When set to true automatically configures Speedbar to observe PHP files. Ignores php-file patterns option; fixed to expression \"\\.\\(inc\\|php[s345]?\\)\"" - :group 'php-mode :tag "PHP Mode Speedbar Config" :type 'boolean :set (lambda (sym val) @@ -123,7 +158,6 @@ Ignores php-file patterns option; fixed to expression \"\\.\\(inc\\|php[s345]?\\ (defcustom php-mode-speedbar-open nil "Normally `php-mode' starts with the speedbar closed. Turning this on will open it whenever `php-mode' is loaded." - :group 'php-mode :tag "PHP Mode Speedbar Open" :type 'boolean :set (lambda (sym val) @@ -134,14 +168,12 @@ Turning this on will open it whenever `php-mode' is loaded." (define-obsolete-variable-alias 'php-template-compatibility 'php-mode-template-compatibility "1.20.0") (defcustom php-mode-template-compatibility t "Should detect presence of html tags." - :group 'php-mode :tag "PHP Mode Template Compatibility" :type 'boolean) (define-obsolete-variable-alias 'php-lineup-cascaded-calls 'php-mode-lineup-cascaded-calls "1.20.0") (defcustom php-mode-lineup-cascaded-calls nil "Indent chained method calls to the previous line." - :group 'php-mode :tag "PHP Mode Lineup Cascaded Calls" :type 'boolean) @@ -151,10 +183,17 @@ Turning this on will open it whenever `php-mode' is loaded." (or "namespace" "function" "class" "trait" "interface") symbol-end)) "Regexp describing line-beginnings that PHP declaration statements." - :group 'php-mode :tag "PHP Mode Page Delimiter" :type 'regexp) +(defcustom php-mode-replace-flymake-diag-function + (eval-when-compile (when (boundp 'flymake-diagnostic-functions) + #'php-flymake)) + "Flymake function to replace, if NIL do not replace." + :tag "PHP Mode Replace Flymake Diag Function" + :type '(choice function + (const :tag "Disable to replace" nil))) + (define-obsolete-variable-alias 'php-do-not-use-semantic-imenu 'php-mode-do-not-use-semantic-imenu "1.20.0") (defcustom php-mode-do-not-use-semantic-imenu t "Customize `imenu-create-index-function' for `php-mode'. @@ -164,7 +203,6 @@ set to `semantic-create-imenu-index' due to `c-mode' being its parent. Set this variable to t if you want to use `imenu-default-create-index-function' even with `semantic-mode' enabled." - :group 'php-mode :tag "PHP Mode Do Not Use Semantic Imenu" :type 'boolean) @@ -175,44 +213,37 @@ enabled." (defcustom php-mode-hook nil "List of functions to be executed on entry to `php-mode'." - :group 'php-mode :tag "PHP Mode Hook" :type 'hook) (defcustom php-mode-pear-hook nil "Hook called when a PHP PEAR file is opened with `php-mode'." - :group 'php-mode :tag "PHP Mode Pear Hook" :type 'hook) (defcustom php-mode-drupal-hook nil "Hook called when a Drupal file is opened with `php-mode'." - :group 'php-mode :tag "PHP Mode Drupal Hook" :type 'hook) (defcustom php-mode-wordpress-hook nil "Hook called when a WordPress file is opened with `php-mode'." - :group 'php-mode :tag "PHP Mode WordPress Hook" :type 'hook) (defcustom php-mode-symfony2-hook nil "Hook called when a Symfony2 file is opened with `php-mode'." - :group 'php-mode :tag "PHP Mode Symfony2 Hook" :type 'hook) (defcustom php-mode-psr2-hook nil "Hook called when a PSR-2 file is opened with `php-mode'." - :group 'php-mode :tag "PHP Mode PSR-2 Hook" :type 'hook) (defcustom php-mode-force-pear nil "Normally PEAR coding rules are enforced only when the filename contains \"PEAR\". Turning this on will force PEAR rules on all PHP files." - :group 'php-mode :tag "PHP Mode Force Pear" :type 'boolean) @@ -221,9 +252,8 @@ Turning this on will force PEAR rules on all PHP files." mumamo-mode turned on. Detects if there are any HTML tags in the buffer before warning, but this is is not very smart; e.g. if you have any tags inside a PHP string, it will be fooled." - :group 'php-mode :tag "PHP Mode Warn If MuMaMo Off" - :type '(choice (const :tag "Warn" t) (const "Don't warn" nil))) + :type '(choice (const :tag "Warn" t) (const :tag "Don't warn" nil))) (defcustom php-mode-coding-style 'pear "Select default coding style to use with `php-mode'. @@ -235,7 +265,6 @@ This variable can take one of the following symbol values: `Drupal' - use coding styles preferred for working with Drupal projects. `WordPress' - use coding styles preferred for working with WordPress projects. `Symfony2' - use coding styles preferred for working with Symfony2 projects." - :group 'php-mode :tag "PHP Mode Coding Style" :type '(choice (const :tag "Default" php) (const :tag "PEAR" pear) @@ -243,7 +272,7 @@ This variable can take one of the following symbol values: (const :tag "WordPress" wordpress) (const :tag "Symfony2" symfony2) (const :tag "PSR-2" psr2)) - :initialize 'custom-initialize-default) + :initialize #'custom-initialize-default) ;; Since this function has a bad influence on the environment of many users, ;; temporarily disable it @@ -252,7 +281,6 @@ This variable can take one of the following symbol values: If you want to suppress styles from being overwritten by directory / file local variables, set NIL." - :group 'php-mode :tag "PHP Mode Enable Project Coding Style" :type 'boolean) @@ -261,49 +289,38 @@ local variables, set NIL." This function may interfere with other hooks and other behaviors. In that case set to `NIL'." - :group 'php-mode :tag "PHP Mode Enable Backup Style Variables" :type 'boolean) -(defcustom php-mode-disable-c-auto-align-backslashes t - "When set to non-NIL, override `c-auto-align-backslashes' to NIL." - :group 'php-mode - :tag "PHP Mode Disable c-auto-align-backslashes" - :type 'boolean) - -(define-obsolete-variable-alias 'php-mode-disable-parent-mode-hooks 'php-mode-disable-c-mode-hook "1.21.0") -(defcustom php-mode-disable-c-mode-hook t - "When set to `T', do not run hooks of parent modes (`java-mode', `c-mode')." - :group 'php-mode - :tag "PHP Mode Disable C Mode Hook" - :type 'boolean) - (defcustom php-mode-enable-project-local-variable t "When set to `T', apply project local variable to buffer local variable." - :group 'php-mode :tag "PHP Mode Enable Project Local Variable" :type 'boolean) -(defconst php-mode-cc-vertion +(defconst php-mode-cc-version (eval-when-compile c-version)) -(defun php-mode-version () - "Display string describing the version of PHP Mode." - (interactive) - (let ((fmt (eval-when-compile (let ((id "$Id$")) - (concat "PHP Mode %s" - (if (string= id (concat [?$ ?I ?d ?$])) - "" - (concat " " id))))))) +(cl-defun php-mode-version (&key as-number) + "Display string describing the version of PHP Mode. + +Although this is an interactive command, it returns a string when called +as a function. Call with AS-NUMBER keyword to compare by `version<'. + +\(version<= \"1.24.1\" (php-mode-version :as-number t))" + (interactive (list :as-number nil)) + (if as-number + (save-match-data (and (string-match (rx (group (+ (in ".0-9")))) php-mode-version-id) + (match-string 0 php-mode-version-id))) (funcall (if (called-interactively-p 'interactive) #'message #'format) - fmt php-mode-version-number))) + "PHP Mode v%s" php-mode-version-id))) ;;;###autoload (define-obsolete-variable-alias 'php-available-project-root-files 'php-project-available-root-files "1.19.0") (defvar php-mode-map (let ((map (make-sparse-keymap "PHP Mode"))) + (set-keymap-parent map c-mode-base-map) ;; Remove menu item for c-mode (define-key map [menu-bar C] nil) @@ -316,7 +333,7 @@ In that case set to `NIL'." ;; ;; Changing the default to mark-defun provides behavior that users ;; are more likely to expect. - (define-key map (kbd "C-M-h") 'mark-defun) + (define-key map (kbd "C-M-h") #'mark-defun) ;; Many packages based on cc-mode provide the 'C-c C-w' binding ;; to toggle Subword Mode. See the page @@ -324,7 +341,7 @@ In that case set to `NIL'." ;; https://www.gnu.org/software/emacs/manual/html_node/ccmode/Subword-Movement.html ;; ;; for more information about Subword mode. - (define-key map (kbd "C-c C-w") 'subword-mode) + (define-key map (kbd "C-c C-w") #'subword-mode) ;; We inherit c-beginning-of-defun and c-end-of-defun from CC Mode ;; but we have two replacement functions specifically for PHP. We @@ -332,19 +349,19 @@ In that case set to `NIL'." ;; key-bindings so that our PHP-specific versions will work even ;; if the user has reconfigured their keys, e.g. if they rebind ;; c-end-of-defun to something other than C-M-e. - (define-key map [remap c-beginning-of-defun] 'php-beginning-of-defun) - (define-key map [remap c-end-of-defun] 'php-end-of-defun) - (define-key map [remap c-set-style] 'php-set-style) - - (define-key map [(control c) (control f)] 'php-search-documentation) - (define-key map [(meta tab)] 'php-complete-function) - (define-key map [(control c) (control m)] 'php-browse-manual) - (define-key map [(control .)] 'php-show-arglist) - (define-key map [(control c) (control r)] 'php-send-region) + (define-key map [remap c-beginning-of-defun] #'php-beginning-of-defun) + (define-key map [remap c-end-of-defun] #'php-end-of-defun) + (define-key map [remap c-set-style] #'php-set-style) + + (define-key map [(control c) (control f)] #'php-search-documentation) + (define-key map [(meta tab)] #'php-complete-function) + (define-key map [(control c) (control m)] #'php-browse-manual) + (define-key map [(control .)] #'php-show-arglist) + (define-key map [(control c) (control r)] #'php-send-region) ;; Use the Emacs standard indentation binding. This may upset c-mode ;; which does not follow this at the moment, but I see no better ;; choice. - (define-key map [tab] 'indent-for-tab-command) + (define-key map "\t" nil) ;Hide CC-mode's `TAB' binding. map) "Keymap for `php-mode'.") @@ -410,7 +427,8 @@ In that case set to `NIL'." (left-assoc "and") (left-assoc "xor") (left-assoc "or") - (left-assoc ","))) + (left-assoc ",") + (left-assoc "=>"))) ;; Allow '\' when scanning from open brace back to defining ;; construct like class @@ -453,10 +471,10 @@ PHP does not have an C-like \"enum\" keyword." php nil) (c-lang-defconst c-typeless-decl-kwds - php (append (c-lang-const c-class-decl-kwds) '("function"))) + php (append (c-lang-const c-class-decl-kwds php) '("function" "const"))) (c-lang-defconst c-modifier-kwds - php '("abstract" "const" "final" "static" "case" "readonly")) + php '("abstract" "final" "static" "case" "readonly")) (c-lang-defconst c-protection-kwds "Access protection label keywords in classes." @@ -572,9 +590,6 @@ might be to handle switch and goto labels differently." php (cl-remove-if (lambda (elm) (and (listp elm) (memq 'c-annotation-face elm))) (c-lang-const c-basic-matchers-after php))) -(c-lang-defconst c-opt-<>-sexp-key - php nil) - (defconst php-mode--re-return-typed-closure (eval-when-compile (rx symbol-start "function" symbol-end @@ -606,19 +621,50 @@ might be to handle switch and goto labels differently." (defun php-lineup-cascaded-calls (langelem) "Line up chained methods using `c-lineup-cascaded-calls', but only if the setting is enabled." - (if php-mode-lineup-cascaded-calls - (c-lineup-cascaded-calls langelem) - (save-excursion + (cond + (php-mode-lineup-cascaded-calls (c-lineup-cascaded-calls langelem)) + ((assq 'arglist-cont-nonempty c-syntactic-context) nil) + ((assq 'defun-block-intro c-syntactic-context) nil) + ((assq 'defun-close c-syntactic-context) nil) + ((assq 'statement-cont c-syntactic-context) nil) + ((save-excursion (beginning-of-line) - (if (looking-at-p "\\s-*->") '+ nil)))) - -(defun php-c-looking-at-or-maybe-in-bracelist (&optional _containing-sexp lim) + (let ((beginning-of-langelem (cdr langelem)) + (beginning-of-current-line (point)) + start) + (skip-chars-forward " ") + (cond + ((looking-at-p "->") '+) + ((looking-at-p "[:?]") '+) + ((looking-at-p "[,;]") nil) + ((looking-at-p "//") nil) + ;; Is the previous line terminated with `,' ? + ((progn + (forward-line -1) + (end-of-line) + (skip-chars-backward " ") + (backward-char 1) + (while (and (< beginning-of-langelem (point)) + (setq start (php-in-string-or-comment-p))) + (goto-char start) + (skip-chars-backward " \r\n") + (backward-char 1)) + (and (not (eq (point) beginning-of-current-line)) + (not (php-in-string-or-comment-p)) + (not (looking-at-p ",")) + (save-excursion + (backward-char) (not (looking-at-p ","))))) + '+) + (t nil))))))) + +(defun php-c-looking-at-or-maybe-in-bracelist (orig-fun &optional containing-sexp lim &rest args) "Replace `c-looking-at-or-maybe-in-bracelist'. CONTAINING-SEXP is the position of the brace/paren/bracket enclosing POINT, or nil if there is no such position, or we do not know it. LIM is a backward search limit." (cond + ((not (derived-mode-p 'php-mode)) (apply orig-fun containing-sexp lim args)) ((looking-at-p "{") (save-excursion (c-backward-token-2 2 t lim) @@ -664,8 +710,9 @@ a backward search limit." "pear" '("php" (c-basic-offset . 4) - (c-offsets-alist . ((case-label . 0))) - (tab-width . 4))) + (c-offsets-alist . ((case-label . 0) (statement-cont . +))) + (tab-width . 4) + (php-mode-lineup-cascaded-calls . nil))) (defun php-enable-pear-coding-style () "Set up `php-mode' to use the coding styles preferred for PEAR code and modules." @@ -794,12 +841,12 @@ plain `php-mode'. To get indentation to work you must use an Emacs library that supports 'multiple major modes' in a buffer. Parts of the buffer will then be in `php-mode' and parts in for example `html-mode'. Known such libraries are:\n\t" - (mapconcat 'identity known-names ", ") + (mapconcat #'identity known-names ", ") "\n" (if available-multi-libs (concat "You have these available in your `load-path':\n\t" - (mapconcat 'identity available-names ", ") + (mapconcat #'identity available-names ", ") "\n\n" "Do you want to turn any of those on? ") "You do not have any of those in your `load-path'."))) @@ -851,9 +898,6 @@ reported, even if `c-report-syntactic-errors' is non-nil." php-warned-bad-indent (php-check-html-for-indentation)) (let ((here (point)) - (c-auto-align-backslashes - (unless php-mode-disable-c-auto-align-backslashes - c-auto-align-backslashes)) doit) (move-beginning-of-line nil) ;; Don't indent heredoc end mark @@ -898,8 +942,8 @@ This is was done due to the problem reported here: (defun php-lineup-string-cont (langelem) "Line up string toward equal sign or dot. e.g. -$str = 'some' - . 'string'; +$str = \\='some' + . \\='string'; this ^ lineup" (save-excursion (goto-char (cdr langelem)) @@ -949,20 +993,19 @@ HEREDOC-START." (eval-and-compile (defconst php-syntax-propertize-rules - `((php-heredoc-start-re - (0 (ignore (php--syntax-propertize-heredoc - (match-beginning 0) - (or (match-string 1) (match-string 2) (match-string 3)) - (null (match-string 3)))))) - (,(rx "#[") - (0 (ignore (php--syntax-propertize-attributes (match-beginning 0))))) - (,(rx (or "'" "\"" )) - (0 (ignore (php--syntax-propertize-quotes-in-comment (match-beginning 0))))))) - - (defmacro php-build-propertize-function () - `(byte-compile (syntax-propertize-rules ,@php-syntax-propertize-rules))) - - (defalias 'php-syntax-propertize-function (php-build-propertize-function))) + (syntax-propertize-precompile-rules + (php-heredoc-start-re + (0 (ignore (php--syntax-propertize-heredoc + (match-beginning 0) + (or (match-string 1) (match-string 2) (match-string 3)) + (null (match-string 3)))))) + ((rx "#[") + (0 (ignore (php--syntax-propertize-attributes (match-beginning 0))))) + ((rx (or "'" "\"")) + (0 (ignore (php--syntax-propertize-quotes-in-comment (match-beginning 0)))))))) + +(defalias 'php-syntax-propertize-function + (syntax-propertize-rules php-syntax-propertize-rules)) (defun php--syntax-propertize-heredoc (start id _is-heredoc) "Apply propertize Heredoc and Nowdoc from START, with ID and IS-HEREDOC." @@ -996,17 +1039,24 @@ HEREDOC-START." (unwind-protect (let (new-start new-end) (goto-char start) + ;; Consider bounding this backwards search by `beginning-of-defun'. + ;; (Benchmarking for a wide range of cases may be needed to decide + ;; whether that's an improvement, as `php-beginning-of-defun' also + ;; uses `re-search-backward'.) (when (re-search-backward php-heredoc-start-re nil t) (let ((maybe (point))) (when (and (re-search-forward (php-heredoc-end-re (match-string 0)) nil t) (> (point) start)) - (setq new-start maybe)))) - (goto-char end) - (when (re-search-backward php-heredoc-start-re nil t) - (if (re-search-forward (php-heredoc-end-re (match-string 0)) nil t) + (setq new-start maybe) (when (> (point) end) - (setq new-end (point))) - (setq new-end (point-max)))) + (setq new-end (point)))))) + (unless new-end + (goto-char end) + (when (re-search-backward php-heredoc-start-re start t) + (if (re-search-forward (php-heredoc-end-re (match-string 0)) nil t) + (when (> (point) end) + (setq new-end (point))) + (setq new-end (point-max))))) (when (or new-start new-end) (cons (or new-start start) (or new-end end)))) ;; Cleanup @@ -1035,13 +1085,7 @@ Borrow the `interactive-form' from `c-set-style' and use the original `c-set-style' function to set all declared stylevars. For compatibility with `c-set-style' pass DONT-OVERRIDE to it. -After setting the stylevars run hooks according to STYLENAME - - \"pear\" `php-mode-pear-hook' - \"drupal\" `php-mode-drupal-hook' - \"wordpress\" `php-mode-wordpress-hook' - \"symfony2\" `php-mode-symfony2-hook' - \"psr2\" `php-mode-psr2-hook'" +After setting the stylevars run hook `php-mode-STYLENAME-hook'." (interactive (list (let ((completion-ignore-case t) (prompt (format "Which %s indentation style? " @@ -1065,15 +1109,10 @@ After setting the stylevars run hooks according to STYLENAME ;; Restore variables (cl-loop for (name . value) in backup-vars do (set (make-local-variable name) value))) - - (if (eq (symbol-value 'php-style-delete-trailing-whitespace) t) - (add-hook 'before-save-hook 'delete-trailing-whitespace nil t) - (remove-hook 'before-save-hook 'delete-trailing-whitespace t)) - (cond ((equal stylename "pear") (run-hooks 'php-mode-pear-hook)) - ((equal stylename "drupal") (run-hooks 'php-mode-drupal-hook)) - ((equal stylename "wordpress") (run-hooks 'php-mode-wordpress-hook)) - ((equal stylename "symfony2") (run-hooks 'php-mode-symfony2-hook)) - ((equal stylename "psr2") (run-hooks 'php-mode-psr2-hook)))) + (if (eq php-style-delete-trailing-whitespace t) + (add-hook 'before-save-hook #'delete-trailing-whitespace nil t) + (remove-hook 'before-save-hook #'delete-trailing-whitespace t)) + (run-hooks (intern (format "php-mode-%s-hook" stylename)))) (defun php-mode--disable-delay-set-style (&rest _args) "Disable `php-mode-set-style-delay' on after hook. ARGS be ignore." @@ -1106,26 +1145,29 @@ After setting the stylevars run hooks according to STYLENAME table)) ;;;###autoload -(define-derived-mode php-mode c-mode "PHP" +(define-derived-mode php-mode php-base-mode "PHP" "Major mode for editing PHP code. \\{php-mode-map}" :syntax-table php-mode-syntax-table - ;; :after-hook (c-update-modeline) - ;; (setq abbrev-mode t) - - (unless (string= php-mode-cc-vertion c-version) - (user-error "CC Mode has been updated. %s" - (if (package-installed-p 'php-mode) - "Please run `M-x package-reinstall php-mode' command." - "Please byte recompile PHP Mode files."))) - - (when php-mode-disable-c-mode-hook - (setq-local c-mode-hook nil) - (setq-local java-mode-hook nil)) + :after-hook (progn (c-make-noise-macro-regexps) + (c-make-macro-with-semi-re) + (c-update-modeline)) + (unless (string= php-mode-cc-version c-version) + (php-mode-debug-reinstall nil)) + (c-initialize-cc-mode t) + (setq abbrev-mode t) + + ;; Must be called once as c-mode to enable font-lock for Heredoc. + ;; TODO: This call may be removed in the future. + (c-common-init 'c-mode) + (c-init-language-vars php-mode) (c-common-init 'php-mode) + (cc-imenu-init cc-imenu-c-generic-expression) + + (setq-local c-auto-align-backslashes nil) (setq-local comment-start "// ") (setq-local comment-start-skip @@ -1166,7 +1208,7 @@ After setting the stylevars run hooks according to STYLENAME (progn (add-hook 'hack-local-variables-hook #'php-mode-set-style-delay t t) (setq php-mode--delayed-set-style t) - (advice-add #'c-set-style :after #'php-mode--disable-delay-set-style '(local))) + (advice-add 'c-set-style :after #'php-mode--disable-delay-set-style)) (let ((php-mode-enable-backup-style-variables nil)) (php-set-style (symbol-name php-mode-coding-style)))) @@ -1176,8 +1218,8 @@ After setting the stylevars run hooks according to STYLENAME (string-match "\\.php\\'" buffer-file-name))) (php-set-style "pear")) - (setq indent-line-function 'php-cautious-indent-line) - (setq indent-region-function 'php-cautious-indent-region) + (setq indent-line-function #'php-cautious-indent-line) + (setq indent-region-function #'php-cautious-indent-region) (setq c-at-vsemi-p-fn #'php-c-at-vsemi-p) (setq c-vsemi-status-unknown-p-fn #'php-c-vsemi-status-unknown-p) @@ -1187,8 +1229,8 @@ After setting the stylevars run hooks according to STYLENAME ;; following two local variables, but we keep them for now until we ;; are completely sure their removal will not break any current ;; behavior or backwards compatibility. - (setq-local beginning-of-defun-function 'php-beginning-of-defun) - (setq-local end-of-defun-function 'php-end-of-defun) + (setq-local beginning-of-defun-function #'php-beginning-of-defun) + (setq-local end-of-defun-function #'php-end-of-defun) (setq-local open-paren-in-column-0-is-defun-start nil) (setq-local defun-prompt-regexp @@ -1196,18 +1238,19 @@ After setting the stylevars run hooks according to STYLENAME (setq-local add-log-current-defun-function nil) (setq-local add-log-current-defun-header-regexp php-beginning-of-defun-regexp) - (when (fboundp 'c-looking-at-or-maybe-in-bracelist) - (advice-add #'c-looking-at-or-maybe-in-bracelist - :override 'php-c-looking-at-or-maybe-in-bracelist '(local))) - (advice-add #'fixup-whitespace :after #'php-mode--fixup-whitespace-after '(local)) + (when (and (eval-when-compile (boundp 'flymake-diagnostic-functions)) + php-mode-replace-flymake-diag-function) + (add-hook 'flymake-diagnostic-functions php-mode-replace-flymake-diag-function nil t)) - (when (>= emacs-major-version 25) - (with-silent-modifications - (save-excursion - (let* ((start (point-min)) - (end (min (point-max) - (+ start syntax-propertize-chunk-size)))) - (php-syntax-propertize-function start end)))))) + (advice-add 'c-looking-at-or-maybe-in-bracelist + :around 'php-c-looking-at-or-maybe-in-bracelist) + (advice-add 'fixup-whitespace :after #'php-mode--fixup-whitespace-after) + + (advice-add 'acm-backend-tabnine-candidate-expand + :filter-args #'php-acm-backend-tabnine-candidate-expand-filter-args) + + (when (eval-when-compile (>= emacs-major-version 25)) + (syntax-ppss-flush-cache (point-min)))) (declare-function semantic-create-imenu-index "semantic/imenu" (&optional stream)) @@ -1218,6 +1261,16 @@ After setting the stylevars run hooks according to STYLENAME #'semantic-create-imenu-index) "Imenu index function for PHP.") +(when (bound-and-true-p consult-imenu-config) + (add-to-list 'consult-imenu-config '(php-mode :toplevel "Namespace" + :types ((?n "Namespace" font-lock-function-name-face) + (?p "Properties" font-lock-type-face) + (?o "Constants" font-lock-type-face) + (?c "Classes" font-lock-type-face) + (?f "Functions" font-lock-function-name-face) + (?i "Imports" font-lock-type-face) + (?m "Methods" font-lock-function-name-face))))) + (autoload 'php-local-manual-complete-function "php-local-manual") (defun php-complete-function () @@ -1248,15 +1301,33 @@ for \\[find-tag] (which see)." (message "Unknown function: %s" tagname)))) ;; Font Lock -(defconst php-phpdoc-type-keywords - (list "string" "integer" "int" "boolean" "bool" "float" - "double" "object" "mixed" "array" "resource" - "void" "null" "false" "true" "self" "static" - "callable" "iterable" "number")) +(defconst php-phpdoc-type-names + '(;; PHPStan and Psalm types + "__stringandstringable" "array" "array-key" "associative-array" "bool" "boolean" + "callable" "callable-array" "callable-object" "callable-string" "class-string" + "closed-resource" "double" "empty" "empty-scalar" "enum-string" "false" "float" + "int" "integer" "interface-string" "iterable" "list" "literal-string" "lowercase-string" + "mixed" "negative-int" "never" "never-return" "never-returns" "no-return" "non-empty-array" + "non-empty-list" "non-empty-literal-string" "non-empty-lowercase-string" "non-empty-mixed" + "non-empty-scalar" "non-empty-string" "non-empty-uppercase-string" "non-falsy-string" + "non-negative-int" "non-positive-int" "non-zero-int" "noreturn" "null" "number" "numeric" + "numeric-string" "object" "open-resource" "parent" "positive-int" "pure-callable" + "pure-closure" "resource" "scalar" "self" "static" "string" "trait-string" "true" + "truthy-string" "uppercase-string" "void" + ;; PHPStan Generic Types + "key-of" "value-of" "int-mask-of" "int-mask" "__benevolent" "template-type" "new") + "A list of type and pseudotype names that can be used in PHPDoc.") + +(make-obsolete-variable 'php-phpdoc-type-keywords 'php-phpdoc-type-names "1.24.2") (defconst php-phpdoc-type-tags (list "package" "param" "property" "property-read" "property-write" - "return" "throws" "var")) + "return" "throws" "var" "self-out" "this-out" "param-out" + "type" "extends" "require-extends" "implemtents" "require-implements" + "template" "template-covariant" "template-extends" "template-implements" + "require-extends" "require-implements" + "assert" "assert-if-true" "assert-if-false" "if-this-is") + "A list of tags specifying type names.") (defconst php-phpdoc-font-lock-doc-comments `(("{@[-[:alpha:]]+\\s-*\\([^}]*\\)}" ; "{@foo ...}" markup. @@ -1266,15 +1337,13 @@ for \\[find-tag] (which see)." (1 'php-doc-variable-sigil prepend nil) (2 'php-variable-name prepend nil)) ("\\(\\$\\)\\(this\\)\\>" (1 'php-doc-$this-sigil prepend nil) (2 'php-doc-$this prepend nil)) - (,(concat "\\s-@" (regexp-opt php-phpdoc-type-tags) "\\s-+" + (,(concat "\\s-@" (rx (? (or "phan" "phpstan" "psalm") "-")) (regexp-opt php-phpdoc-type-tags) "\\s-+" "\\(" (rx (+ (? "?") (? "\\") (+ (in "0-9A-Z_a-z")) (? "[]") (? "|"))) "\\)+") 1 'php-string prepend nil) (,(concat "\\(?:|\\|\\?\\|\\s-\\)\\(" - (regexp-opt php-phpdoc-type-keywords 'words) + (regexp-opt php-phpdoc-type-names 'words) "\\)") 1 font-lock-type-face prepend nil) - ("https?://[^\n\t ]+" - 0 'link prepend nil) ("^\\(?:/\\*\\)?\\(?:\\s \\|\\*\\)*\\(@[[:alpha:]][-[:alpha:]\\]*\\)" ; "@foo ..." markup. 1 'php-doc-annotation-tag prepend nil))) @@ -1307,14 +1376,14 @@ for \\[find-tag] (which see)." ;; Highlight variables, e.g. 'var' in '$var' and '$obj->var', but ;; not in $obj->var() - ("\\(->\\)\\(\\sw+\\)\\s-*(" (1 'php-object-op) (2 'php-method-call)) + ("\\(->\\)\\(\\sw+\\)\\s-*(" (1 'php-object-op) (2 php-method-call)) ("\\<\\(const\\)\\s-+\\(\\_<.+?\\_>\\)" (1 'php-keyword) (2 'php-constant-assign)) ;; Logical operator (!) ("\\(!\\)[^=]" 1 'php-logical-op) ;; Highlight special variables - ("\\(\\$\\)\\(this\\)\\>" (1 'php-$this-sigil) (2 'php-$this)) + ("\\(\\$\\)\\(this\\)\\>" (1 'php-this-sigil) (2 'php-this)) ("\\(\\$+\\)\\(\\sw+\\)" (1 'php-variable-sigil) (2 'php-variable-name)) ("\\(->\\)\\([a-zA-Z0-9_]+\\)" (1 'php-object-op) (2 'php-property-name)) @@ -1349,7 +1418,7 @@ for \\[find-tag] (which see)." ;; Highlight static method calls as such. This is necessary for method ;; names which are identical to keywords to be highlighted correctly. - ("\\sw+::\\(\\sw+\\)(" 1 'php-static-method-call) + ("\\sw+::\\(\\sw+\\)(" 1 php-static-method-call) ;; Multiple catch (FooException | BarException $e) (,(rx symbol-start "catch" symbol-end (* (syntax whitespace)) "(" (* (syntax whitespace)) @@ -1382,8 +1451,18 @@ for \\[find-tag] (which see)." ;; is usually overkill. `( ("\\<\\(@\\)" 1 'php-errorcontrol-op) + ;; import function statement + (,(rx symbol-start (group "use" (+ (syntax whitespace)) "function") + (+ (syntax whitespace))) + (1 'php-import-declaration) + (,(rx (group (+ (or (syntax word) (syntax symbol) "\\" "{" "}")))) nil nil (1 'php-function-name t))) + ;; import constant statement + (,(rx symbol-start (group "use" (+ (syntax whitespace)) "const") + (+ (syntax whitespace))) + (1 'php-import-declaration) + (,(rx (group (+ (or (syntax word) (syntax symbol) "\\" "{" "}")))) nil nil (1 'php-constant-assign t))) ;; Highlight function calls - ("\\(\\_<\\(?:\\sw\\|\\s_\\)+?\\_>\\)\\s-*(" 1 'php-function-call) + ("\\(\\_<\\(?:\\sw\\|\\s_\\)+?\\_>\\)\\s-*(" 1 php-function-call) ;; Highlight all upper-cased symbols as constant ("\\<\\([A-Z_][A-Z0-9_]+\\)\\>" 1 'php-constant) @@ -1441,18 +1520,18 @@ for \\[find-tag] (which see)." ;; Not operator (!) is defined in "before cc-mode" section above. ("\\(&&\\|||\\)" 1 'php-logical-op) ;; string interpolation ("$var, ${var}, {$var}") - (php-mode--string-interpolated-variable-font-lock-find 0 nil))) + (php-mode--string-interpolated-variable-font-lock-find 0 nil) + (,(rx symbol-start (group (or "get" "set")) (+ (syntax whitespace)) (or "{" "=>")) + 1 'php-builtin))) "Detailed highlighting for PHP Mode.") (defvar php-font-lock-keywords php-font-lock-keywords-3 "Default expressions to highlight in PHP Mode.") -(add-to-list - (eval-when-compile - (if (boundp 'flymake-proc-allowed-file-name-masks) - 'flymake-proc-allowed-file-name-masks - 'flymake-allowed-file-name-masks)) - '("\\.php[345s]?\\'" php-flymake-php-init)) +(eval-when-compile + (unless (boundp 'flymake-proc-allowed-file-name-masks) + (add-to-list 'flymake-allowed-file-name-masks + '("\\.php[345s]?\\'" php-flymake-php-init)))) (defun php-send-region (start end) @@ -1486,11 +1565,25 @@ The output will appear in the buffer *PHP*." ;;; logic of `fixup-whitespace'. (defun php-mode--fixup-whitespace-after () "Remove whitespace before certain characters in PHP Mode." - (when (or (looking-at-p " \\(?:;\\|,\\|->\\|::\\)") - (save-excursion - (forward-char -2) - (looking-at-p "->\\|::"))) + (when (and (derived-mode-p 'php-mode) + (or (looking-at-p " \\(?:;\\|,\\|->\\|::\\)") + (save-excursion + (forward-char -2) + (looking-at-p "->\\|::")))) (delete-char 1))) + +;; Advice for lsp-bridge' acm-backend-tabnine +;; see https://github.com/manateelazycat/lsp-bridge/issues/402#issuecomment-1305653058 +(defun php-acm-backend-tabnine-candidate-expand-filter-args (args) + "Adjust to replace bound-start ARGS for Tabnine in PHP." + (if (not (derived-mode-p 'php-mode)) + args + (cl-multiple-value-bind (candidate-info bound-start) args + (save-excursion + (goto-char bound-start) + (when (looking-at-p (eval-when-compile (regexp-quote "$"))) + (setq bound-start (1+ bound-start)))) + (list candidate-info bound-start)))) ;;;###autoload (progn diff --git a/lisp/php-project.el b/lisp/php-project.el index 3ac833ba..0b59e2ce 100644 --- a/lisp/php-project.el +++ b/lisp/php-project.el @@ -1,11 +1,11 @@ ;;; php-project.el --- Project support for PHP application -*- lexical-binding: t; -*- -;; Copyright (C) 2022 Friends of Emacs-PHP development +;; Copyright (C) 2023 Friends of Emacs-PHP development ;; Author: USAMI Kenta ;; Keywords: tools, files ;; URL: https://github.com/emacs-php/php-mode -;; Version: 1.24.1 +;; Version: 1.26.1 ;; License: GPL-3.0-or-later ;; This program is free software; you can redistribute it and/or modify @@ -40,12 +40,6 @@ ;; ;; Return path to PHP executable file with the project settings overriding. ;; -;; ### `php-project-get-phan-executable()' -;; -;; Return path to Phan executable file with the project settings overriding. -;; Phan is a static analyzer and LSP server implementation for PHP. -;; See https://github.com/phan/phan -;; ;; ## `.dir-locals.el' support ;; ;; - `php-project-coding-style' @@ -59,10 +53,6 @@ ;; - `php-project-php-executable' ;; - Path to project specific PHP executable file. ;; - If you want to use a file different from the system wide `php' command. -;; - `php-project-phan-executable' -;; - Path to project specific Phan executable file. -;; - When not specified explicitly, it is automatically searched from -;; Composer's dependency of the project and `exec-path'. ;; ;;; Code: @@ -142,10 +132,6 @@ defines constants, and sets the class loaders.") (put 'php-project-php-executable 'safe-local-variable #'(lambda (v) (and (stringp v) (file-executable-p v)))) - (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) - (defvar-local php-project-coding-style nil "Symbol value of the coding style of the project that PHP major mode refers to. @@ -240,13 +226,6 @@ Typically it is `pear', `drupal', `wordpress', `symfony2' and `psr2'.") ((boundp 'php-executable) php-executable) (t (executable-find "php")))) -(defun php-project-get-phan-executable () - "Return path to phan executable file." - (or (car-safe (php-project--eval-bootstrap-scripts - (list php-project-phan-executable - (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 @@ -288,7 +267,7 @@ Typically it is `pear', `drupal', `wordpress', `symfony2' and `psr2'.") This function is compatible with `project-find-functions'." (let ((default-directory dir)) - (when-let (root (php-project-get-root-dir)) + (when-let* ((root (php-project-get-root-dir))) (if (file-exists-p (expand-file-name ".git" root)) (cons 'vc root) (cons 'transient root))))) diff --git a/lisp/php.el b/lisp/php.el index 94fec3c4..ef46272c 100644 --- a/lisp/php.el +++ b/lisp/php.el @@ -1,10 +1,11 @@ ;;; php.el --- PHP support for friends -*- lexical-binding: t; -*- -;; Copyright (C) 2022 Friends of Emacs-PHP development +;; Copyright (C) 2023 Friends of Emacs-PHP development +;; Copyright (C) 1985, 1987, 1992-2022 Free Software Foundation, Inc. ;; Author: USAMI Kenta ;; Created: 5 Dec 2018 -;; Version: 1.24.1 +;; Version: 1.26.1 ;; Keywords: languages, php ;; Homepage: https://github.com/emacs-php/php-mode ;; License: GPL-3.0-or-later @@ -26,9 +27,16 @@ ;; This file provides common variable and functions for PHP packages. +;; These functions are copied function from GNU Emacs. +;; +;; - c-end-of-token (cc-engine.el) +;; + ;;; Code: (eval-when-compile + (require 'cc-mode) (require 'cl-lib)) +(require 'cc-engine) (require 'flymake) (require 'php-project) (require 'rx) @@ -41,12 +49,24 @@ :link '(url-link :tag "Official Site" "https://github.com/emacs-php/php-mode") :link '(url-link :tag "PHP Mode Wiki" "https://github.com/emacs-php/php-mode/wiki")) -(defcustom php-executable (or (executable-find "php") "/usr/bin/php") +(defcustom php-executable (or (executable-find "php") "php") "The location of the PHP executable." :group 'php :tag "PHP Executable" :type 'string) +(defcustom php-phpdbg-executable (list "phpdbg") + "The location of the PHPDBG executable." + :group 'php + :tag "PHP PHPDBG Executable" + :type '(repeat string)) + +(defcustom php-php-parse-executabe nil + "The location of the php-parse executable." + :group 'php + :tag "PHP php-parse Executable" + :type '(repeat string)) + (defcustom php-site-url "https://www.php.net/" "Default PHP.net site URL. @@ -91,8 +111,8 @@ You can replace \"en\" with your ISO language code." "Function to search PHP Manual at cursor position." :group 'php :tag "PHP Search Documentation Function" - :type '(choice (const :tag "Use online documentation" #'php-search-web-documentation) - (const :tag "Use local documentation" #'php-local-manual-search) + :type '(choice (const :tag "Use online documentation" php-search-web-documentation) + (const :tag "Use local documentation" php-local-manual-search) (function :tag "Use other function"))) (defcustom php-search-documentation-browser-function nil @@ -195,14 +215,49 @@ a completion list." :type 'integer :link '(url-link :tag "Built-in web server" "https://www.php.net/manual/features.commandline.webserver.php")) + +(defcustom php-topsy-separator " > " + "Separator string for `php-topsy-beginning-of-defun-with-class'." + :group 'php + :tag "PHP Topsy Separator" + :type 'string) + +(defcustom php-function-call 'php-function-call-traditional + "Face name to use for method call." + :group 'php + :tag "PHP Function Call" + :type 'face) + +(defcustom php-method-call 'php-method-call-traditional + "Face name to use for method call." + :group 'php + :tag "PHP Method Call" + :type 'face) + +(defcustom php-static-method-call 'php-static-method-call-traditional + "Face name to use for method call." + :group 'php + :tag "PHP Static Method Call" + :type 'face) ;;; PHP Keywords (defconst php-magical-constants - (list "__LINE__" "__FILE__" "__FUNCTION__" "__CLASS__" "__TRAIT__" "__METHOD__" "__NAMESPACE__") + '("__CLASS__" "__DIR__" "__FILE__" "__FUNCTION__" "__LINE__" + "__METHOD__" "__NAMESPACE__" "__TRAIT__") "Magical keyword that is expanded at compile time. These are different from \"constants\" in strict terms. see https://www.php.net/manual/language.constants.predefined.php") + +(defconst php-re-token-symbols + (eval-when-compile + (regexp-opt (list "&" "&=" "array(" "(array)" "&&" "||" "(bool)" "(boolean)" "break;" "?>" "%>" + "??" "??=" ".=" "--" "/=" "=>" "(real)" "(double)" "(float)" "::" "..." + "__halt_compiler()" "++" "(int)" "(integer)" "==" ">=" "===" "!=" "<>" "!==" + "<=" "-=" "%=" "*=" "\\" "(object)" "->" "?->" "" ">>" ">>=" "<<<" "(string)" "^=" "yield from" + "[" "]" "(" ")" "{" "}" ";") + t))) ;;; Utillity for locate language construction (defsubst php-in-string-p () @@ -243,12 +298,12 @@ an integer (the current comment nesting)." "Make a regular expression for methods with the given VISIBILITY. VISIBILITY must be a string that names the visibility for a PHP -method, e.g. 'public'. The parameter VISIBILITY can itself also +method, e.g. `public'. The parameter VISIBILITY can itself also be a regular expression. The regular expression this function returns will check for other -keywords that can appear in method signatures, e.g. 'final' and -'static'. The regular expression will have one capture group +keywords that can appear in method signatures, e.g. `final' and +`static'. The regular expression will have one capture group which will be the name of the method." (when (stringp visibility) (setq visibility (list visibility))) @@ -275,19 +330,25 @@ which will be the name of the method." '((* any) line-end)))))) (defun php-create-regexp-for-classlike (type) - "Accepts a `TYPE' of a 'classlike' object as a string, such as -'class' or 'interface', and returns a regexp as a string which + "Accepts a `TYPE' of a `classlike' object as a string, such as +`class' or `interface', and returns a regexp as a string which can be used to match against definitions for that classlike." (concat ;; First see if 'abstract' or 'final' appear, although really these ;; are not valid for all values of `type' that the function ;; accepts. - "^\\s-*\\(?:\\(?:abstract\\|final\\)\\s-+\\)?" + (eval-when-compile + (rx line-start + (* (syntax whitespace)) + (? (or "abstract" "final" "readonly") + (+ (syntax whitespace))))) ;; The classlike type type ;; Its name, which is the first captured group in the regexp. We ;; allow backslashes in the name to handle namespaces, but again ;; this is not necessarily correct for all values of `type'. + ;; (rx (+ (syntax whitespace)) + ;; (group (+ (or (syntax word) "\\" (syntax symbol))))) "\\s-+\\(\\(?:\\sw\\|\\\\\\|\\s_\\)+\\)"))) (defconst php-imenu-generic-expression-default @@ -410,7 +471,7 @@ can be used to match against definitions for that classlike." (defcustom php-imenu-generic-expression 'php-imenu-generic-expression-default "Default Imenu generic expression for PHP Mode. See `imenu-generic-expression'." - :type '(choice (alist :key-type string :value-type list) + :type '(choice (alist :key-type string :value-type (list string)) (const php-imenu-generic-expression-legacy) (const php-imenu-generic-expression-simple) variable) @@ -422,7 +483,18 @@ can be used to match against definitions for that classlike." (defconst php--re-classlike-pattern (eval-when-compile - (php-create-regexp-for-classlike (regexp-opt '("class" "interface" "trait"))))) + (php-create-regexp-for-classlike (regexp-opt '("class" "interface" "trait" "enum"))))) + +(defvar php--analysis-syntax-table + (eval-when-compile + (let ((table (make-syntax-table))) + (c-populate-syntax-table table) + (modify-syntax-entry ?_ "w" table) + (modify-syntax-entry ?` "\"" table) + (modify-syntax-entry ?\" "\"" table) + (modify-syntax-entry ?# "< b" table) + (modify-syntax-entry ?\n "> b" table) + table))) (defun php-get-current-element (re-pattern) "Return backward matched element by RE-PATTERN." @@ -431,27 +503,87 @@ can be used to match against definitions for that classlike." (when (re-search-backward re-pattern nil t) (match-string-no-properties 1))))) -(defun php-get-pattern () - "Find the pattern we want to complete. -`find-tag-default' from GNU Emacs etags.el" +(eval-and-compile + (if (eval-when-compile (fboundp 'thing-at-point-bounds-of-string-at-point)) + (defalias 'php--thing-at-point-bounds-of-string-at-point #'thing-at-point-bounds-of-string-at-point) + ;; Copyright (C) 1991-1998, 2000-2022 Free Software Foundation, Inc. + ;; Follows function is copied from Emacs 28's thingatpt.el. + ;; https://github.com/emacs-mirror/emacs/commit/2abf143f8185fced544c4f8d144ea710142d7a59 + (defun php--thing-at-point-bounds-of-string-at-point () + "Return the bounds of the string at point. +Prefer the enclosing string with fallback on sexp at point. +\[Internal function used by `bounds-of-thing-at-point'.]" + (save-excursion + (let ((ppss (syntax-ppss))) + (if (nth 3 ppss) + ;; Inside the string + (ignore-errors + (goto-char (nth 8 ppss)) + (cons (point) (progn (forward-sexp) (point)))) + ;; At the beginning of the string + (if (eq (char-syntax (char-after)) ?\") + (let ((bound (bounds-of-thing-at-point 'sexp))) + (and bound + (<= (car bound) (point)) (< (point) (cdr bound)) + bound)))))))) + (if (eval-when-compile (fboundp 'c-end-of-token)) + (defalias 'php--c-end-of-token #'c-end-of-token) + ;; Copyright (C) 1985, 1987, 1992-2022 Free Software Foundation, Inc. + ;; Follows function is copied from Emacs 27's cc-engine.el. + ;; https://emba.gnu.org/emacs/emacs/-/commit/95fb826dc58965eac287c0826831352edf2e56f7 + (defun php--c-end-of-token (&optional back-limit) + ;; Move to the end of the token we're just before or in the middle of. + ;; BACK-LIMIT may be used to bound the backward search; if given it's + ;; assumed to be at the boundary between two tokens. Return non-nil if the + ;; point is moved, nil otherwise. + ;; + ;; This function might do hidden buffer changes. + (let ((start (point))) + (cond ;; ((< (skip-syntax-backward "w_" (1- start)) 0) + ;; (skip-syntax-forward "w_")) + ((> (skip-syntax-forward "w_") 0)) + ((< (skip-syntax-backward ".()" back-limit) 0) + (while (< (point) start) + (if (looking-at c-nonsymbol-token-regexp) + (goto-char (match-end 0)) + ;; `c-nonsymbol-token-regexp' should always match since + ;; we've skipped backward over punctuation or paren + ;; syntax, but move forward in case it doesn't so that + ;; we don't leave point earlier than we started with. + (forward-char)))) + (t (if (looking-at c-nonsymbol-token-regexp) + (goto-char (match-end 0))))) + (> (point) start))))) + +(defun php-leading-tokens (length) + "Return a list of leading LENGTH tokens from cursor point. + +The token list is lined up in the opposite side of the visual arrangement. +The order is reversed by calling as follows: + \(nreverse \(php-leading-tokens 3\)\)" (save-excursion (save-match-data - (while (looking-at "\\sw\\|\\s_") - (forward-char 1)) - (when (or (re-search-backward "\\sw\\|\\s_" - (save-excursion (beginning-of-line) (point)) - t) - (re-search-forward "\\(\\sw\\|\\s_\\)+" - (save-excursion (end-of-line) (point)) - t)) - (goto-char (match-end 0)) - (buffer-substring-no-properties - (point) - (progn - (forward-sexp -1) - (while (looking-at "\\s'") - (forward-char 1)) - (point))))))) + (with-syntax-table php--analysis-syntax-table + (cl-loop + repeat length + do (progn + (forward-comment (- (point))) + (c-backward-token-2 1 nil)) + collect + (cond + ((when-let* ((bounds (php--thing-at-point-bounds-of-string-at-point))) + (prog1 (buffer-substring-no-properties (car bounds) (cdr bounds)) + (goto-char (car bounds))))) + ((looking-at php-re-token-symbols) + (prog1 (match-string-no-properties 0) + (goto-char (match-beginning 0)))) + ((buffer-substring-no-properties (point) + (save-excursion (php--c-end-of-token) (point)))))))))) + +(defun php-get-pattern () + "Find the pattern we want to complete. +`find-tag-default' from GNU Emacs etags.el." + (car (php-leading-tokens 1))) ;;; Provide support for Flymake so that users can see warnings and ;;; errors in real-time as they write code. @@ -460,11 +592,9 @@ can be used to match against definitions for that classlike." This is an alternative function of `flymake-php-init'. Look at the `php-executable' variable instead of the constant \"php\" command." - (let* ((init (funcall (eval-when-compile - (if (fboundp 'flymake-proc-php-init) - 'flymake-proc-php-init - 'flymake-php-init))))) - (list php-executable (cdr init)))) + (let ((init (with-no-warnings (flymake-php-init)))) + (setf (car init) php-executable) + init)) (defconst php-re-detect-html-tag-aggressive (eval-when-compile @@ -532,6 +662,15 @@ Look at the `php-executable' variable instead of the constant \"php\" command." (setq mode nil))) (or mode php-default-major-mode))) +;;;###autoload +(define-derived-mode php-base-mode prog-mode "PHP" + "Generic major mode for editing PHP. + +This mode is intended to be inherited by concrete major modes. +Currently there are `php-mode' and `php-ts-mode'." + :group 'php + nil) + ;;;###autoload (defun php-mode-maybe () "Select PHP mode or other major mode." @@ -543,17 +682,15 @@ Look at the `php-executable' variable instead of the constant \"php\" command." (defun php-current-class () "Insert current class name if cursor in class context." (interactive) - (let ((matched (php-get-current-element php--re-classlike-pattern))) - (when matched - (insert (concat matched php-class-suffix-when-insert))))) + (when-let* ((matched (php-get-current-element php--re-classlike-pattern))) + (insert (concat matched php-class-suffix-when-insert)))) ;;;###autoload (defun php-current-namespace () "Insert current namespace if cursor in namespace context." (interactive) - (let ((matched (php-get-current-element php--re-namespace-pattern))) - (when matched - (insert (concat matched php-namespace-suffix-when-insert))))) + (when-let* ((matched (php-get-current-element php--re-namespace-pattern))) + (insert (concat matched php-namespace-suffix-when-insert)))) ;;;###autoload (defun php-copyit-fqsen () @@ -566,6 +703,33 @@ Look at the `php-executable' variable instead of the constant \"php\" command." (if (string= class "") "" (concat "\\" class "::")) (if (string= namedfunc "") "" (concat namedfunc "()")))))) +(defun php-topsy-beginning-of-defun-with-class () + "Return function signature and class name string for header line in topsy. + +You can add the function to topsy with the code below: + + (add-to-list \\='topsy-mode-functions + \\='(php-mode . php-topsy-beginning-of-defun-with-class))" + (save-excursion + (goto-char (window-start)) + (mapconcat + #'identity + (append + (save-match-data + (save-excursion + (when (re-search-backward php--re-classlike-pattern nil t) + (font-lock-ensure (point) (line-end-position)) + (list (string-trim (buffer-substring (point) (line-end-position))))))) + (progn + (beginning-of-defun) + (font-lock-ensure (point) (line-end-position)) + (list (string-trim + (replace-regexp-in-string + (eval-when-compile (rx bos ". + +### Usage + +``` +Usage: + ./script/extract_functions.php count < php_manual_en.json + ./script/extract_functions.php modules < php_manual_en.json + ./script/extract_functions.php functions < php_manual_en.json > result.json + ./script/extract_functions.php functions-sexp < php_manual_en.json > result.el +``` + +### Data + +A pattern for grouping function modules by function-id is written in `data/module_id_prefixes.php`. + + * All entries are combined as prefix matching patterns + * The meanings of `.` and `\.` (matches) in the regular expression are swapped + * `\.` matches any one character + * `.` matches only `"."` + * An alphanumeric-terminated entry requires an exact match + * Other entries require a [`\b`(word boundary)](https://www.php.net/manual/regexp.reference.escape.php) at the end + +```php +return [ + 'apache' => [ + 'function.apache-', // matches all "apache-" prefixed IDs + 'function.virtual', // matches only "function.virtual" + ], + 'bcmath' => [ + 'function.bc\.+', // matches "function.bcadd", "function.bccomp", ...etc + ], +``` diff --git a/script/data/module_id_prefixes.php b/script/data/module_id_prefixes.php new file mode 100644 index 00000000..93a01f5e --- /dev/null +++ b/script/data/module_id_prefixes.php @@ -0,0 +1,853 @@ + + * @license FSFAP https://spdx.org/licenses/FSFAP.html + */ + +// Copying and distribution of this file, with or without modification, +// are permitted in any medium without royalty provided the copyright +// notice and this notice are preserved. This file is offered as-is, +// without any warranty. + +return [ + 'apache' => [ + 'function.apache-', + 'function.virtual', + ], + 'apcu' => [ + 'function.apcu-', + ], + 'bcmath' => [ + 'function.bc\.+', + ], + 'bzip2' => [ + 'function.bz\.+', + ], + 'calendar' => [ + 'function.cal-', + 'function.easter-date', + 'function.easter-days', + 'function.frenchtojd', + 'function.gregoriantojd', + 'function.jddayofweek', + 'function.jdmonthname', + 'function.jdtofrench', + 'function.jdtogregorian', + 'function.jdtojewish', + 'function.jdtojulian', + 'function.jdtounix', + 'function.jewishtojd', + 'function.unixtojd', + ], + 'commonmark' => [ + 'function.commonmark-', + ], + 'com_dotnet' => [ + 'function.com-', + 'function.variant-', + ], + 'componere' => [ + 'componere.', + ], + 'core' => [ + 'function.abs', + 'function.acos', + 'function.acosh', + 'function.addcslashes', + 'function.addslashes', + 'function.array-', + 'function.arsort', + 'function.asin', + 'function.asinh', + 'function.asort', + 'function.assert-options', + 'function.atan', + 'function.atan2', + 'function.atanh', + 'function.base-convert', + 'function.base64-', + 'function.basename', + 'function.bin2hex', + 'function.bindec', + 'function.boolval', + 'function.call-user-func', + 'function.call-user-func-array', + 'function.ceil', + 'function.chdir', + 'function.checkdate', + 'function.checkdnsrr', + 'function.chgrp', + 'function.chmod', + 'function.chop', + 'function.chown', + 'function.chr', + 'function.chroot', + 'function.chunk-split', + 'function.class-alias', + 'function.class-exists', + 'function.class-implements', + 'function.class-parents', + 'function.class-uses', + 'function.clearstatcache', + 'function.cli-', + 'function.closedir', + 'function.closelog', + 'function.compact', + 'function.connection-', + 'function.constant', + 'function.convert-cyr-string', + 'function.convert-uudecode', + 'function.convert-uuencode', + 'function.copy', + 'function.cos', + 'function.cosh', + 'function.count', + 'function.count-chars', + 'function.crc32', + 'function.create-function', + 'function.crypt', + 'function.ctype-', + 'function.current', + 'function.date', + 'function.date-', + 'function.debug-', + 'function.decbin', + 'function.dechex', + 'function.decoct', + 'function.define', + 'function.defined', + 'function.deg2rad', + 'function.delete', + 'function.die', + 'function.dir', + 'function.dirname', + 'function.disk-', + 'function.diskfreespace', + 'function.dl', + 'function.dns-check-record', + 'function.dns-get-mx', + 'function.dns-get-record', + 'function.doubleval', + 'function.each', + 'function.enum-exists', + 'function.error-', + 'function.escapeshellarg', + 'function.escapeshellcmd', + 'function.eval', + 'function.exec', + 'function.exit', + 'function.exp', + 'function.explode', + 'function.expm1', + 'function.extension-loaded', + 'function.extract', + 'function.fastcgi-', + 'function.fclose', + 'function.fdatasync', + 'function.fdiv', + 'function.feof', + 'function.fflush', + 'function.fgetc', + 'function.fgetcsv', + 'function.fgets', + 'function.fgetss', + 'function.file', + 'function.file\.+', + 'function.filter-', + 'function.finfo-', + 'function.floatval', + 'function.flock', + 'function.floor', + 'function.flush', + 'function.fmod', + 'function.fnmatch', + 'function.fopen', + 'function.forward-static-call', + 'function.forward-static-call-array', + 'function.fpassthru', + 'function.fprintf', + 'function.fputcsv', + 'function.fputs', + 'function.fread', + 'function.fscanf', + 'function.fseek', + 'function.fsockopen', + 'function.fstat', + 'function.fsync', + 'function.ftell', + 'function.ftruncate', + 'function.func-get-arg', + 'function.func-get-args', + 'function.func-num-args', + 'function.function-exists', + 'function.fwrite', + 'function.gc-', + 'function.get-', + 'function.getallheaders', + 'function.getcwd', + 'function.getdate', + 'function.getenv', + 'function.gethostbyaddr', + 'function.gethostbyname', + 'function.gethostbynamel', + 'function.gethostname', + 'function.getlastmod', + 'function.getmxrr', + 'function.getmygid', + 'function.getmyinode', + 'function.getmypid', + 'function.getmyuid', + 'function.getopt', + 'function.getprotobyname', + 'function.getprotobynumber', + 'function.getrandmax', + 'function.getrusage', + 'function.getservbyname', + 'function.getservbyport', + 'function.gettimeofday', + 'function.gettype', + 'function.glob', + 'function.gmdate', + 'function.gmmktime', + 'function.gmstrftime', + 'function.hash', + 'function.hash-', + 'function.header', + 'function.header-register-callback', + 'function.header-remove', + 'function.headers-list', + 'function.headers-sent', + 'function.hebrev', + 'function.hebrevc', + 'function.hex2bin', + 'function.hexdec', + 'function.highlight-file', + 'function.highlight-string', + 'function.hrtime', + 'function.html-entity-decode', + 'function.htmlentities', + 'function.htmlspecialchars', + 'function.htmlspecialchars-decode', + 'function.http-build-query', + 'function.http-response-code', + 'function.hypot', + 'function.idate', + 'function.ignore-user-abort', + 'function.implode', + 'function.in-array', + 'function.inet-', + 'function.ini-', + 'function.intdiv', + 'function.interface-exists', + 'function.intval', + 'function.ip2long', + 'function.is-a', + 'function.is-array', + 'function.is-bool', + 'function.is-callable', + 'function.is-countable', + 'function.is-dir', + 'function.is-double', + 'function.is-executable', + 'function.is-file', + 'function.is-finite', + 'function.is-float', + 'function.is-infinite', + 'function.is-int', + 'function.is-integer', + 'function.is-iterable', + 'function.is-link', + 'function.is-long', + 'function.is-nan', + 'function.is-null', + 'function.is-numeric', + 'function.is-object', + 'function.is-readable', + 'function.is-resource', + 'function.is-scalar', + 'function.is-string', + 'function.is-subclass-of', + 'function.is-uploaded-file', + 'function.is-writable', + 'function.is-writeable', + 'function.iterator-', + 'function.join', + 'function.json-', + 'function.juliantojd', + 'function.key', + 'function.key-exists', + 'function.krsort', + 'function.ksort', + 'function.lcfirst', + 'function.lcg-value', + 'function.lchgrp', + 'function.lchown', + 'function.levenshtein', + 'function.link', + 'function.linkinfo', + 'function.list', + 'function.localeconv', + 'function.localtime', + 'function.log', + 'function.log10', + 'function.log1p', + 'function.long2ip', + 'function.lstat', + 'function.ltrim', + 'function.mail', + 'function.max', + 'function.md5', + 'function.md5-file', + 'function.memory-', + 'function.metaphone', + 'function.method-exists', + 'function.microtime', + 'function.mime-content-type', + 'function.min', + 'function.mkdir', + 'function.mktime', + 'function.money-format', + 'function.move-uploaded-file', + 'function.mt-', + 'function.natcasesort', + 'function.natsort', + 'function.net-get-interfaces', + 'function.next', + 'function.nl-langinfo', + 'function.nl2br', + 'function.number-format', + 'function.ob-', + 'function.octdec', + 'function.opcache-', + 'function.opendir', + 'function.openlog', + 'function.ord', + 'function.output-', + 'function.pack', + 'function.parse-ini-', + 'function.parse-str', + 'function.parse-url', + 'function.passthru', + 'function.password-', + 'function.pathinfo', + 'function.pclose', + 'function.pfsockopen', + 'function.php-ini-', + 'function.php-sapi-name', + 'function.php-strip-whitespace', + 'function.php-uname', + 'function.phpcredits', + 'function.phpdbg-', + 'function.phpinfo', + 'function.phpversion', + 'function.pi', + 'function.popen', + 'function.pos', + 'function.pow', + 'function.preg-', + 'function.prev', + 'function.print-r', + 'function.printf', + 'function.proc-', + 'function.property-exists', + 'function.putenv', + 'function.quoted-printable-', + 'function.quotemeta', + 'function.rad2deg', + 'function.rand', + 'function.random-', + 'function.range', + 'function.rawurldecode', + 'function.rawurlencode', + 'function.readdir', + 'function.readfile', + 'function.readlink', + 'function.realpath', + 'function.realpath-', + 'function.register-shutdown-function', + 'function.register-tick-function', + 'function.rename', + 'function.reset', + 'function.restore-', + 'function.rewind', + 'function.rewinddir', + 'function.rmdir', + 'function.round', + 'function.rsort', + 'function.rtrim', + 'function.sapi-', + 'function.scandir', + 'function.seaslog-get-author', + 'function.seaslog-get-version', + 'function.serialize', + 'function.session-', + 'function.set-', + 'function.setcookie', + 'function.setlocale', + 'function.setrawcookie', + 'function.settype', + 'function.sha1', + 'function.sha1-file', + 'function.shell-exec', + 'function.show-source', + 'function.shuffle', + 'function.similar-text', + 'function.sin', + 'function.sinh', + 'function.sizeof', + 'function.sleep', + 'function.sort', + 'function.soundex', + 'function.spl-', + 'function.sprintf', + 'function.sqrt', + 'function.srand', + 'function.sscanf', + 'function.stat', + 'function.str-', + 'function.strcasecmp', + 'function.strchr', + 'function.strcmp', + 'function.strcoll', + 'function.strcspn', + 'function.stream-', + 'function.strftime', + 'function.strip-tags', + 'function.stripcslashes', + 'function.stripos', + 'function.stripslashes', + 'function.stristr', + 'function.strlen', + 'function.strnatcasecmp', + 'function.strnatcmp', + 'function.strncasecmp', + 'function.strncmp', + 'function.strpbrk', + 'function.strpos', + 'function.strptime', + 'function.strrchr', + 'function.strrev', + 'function.strripos', + 'function.strrpos', + 'function.strspn', + 'function.strstr', + 'function.strtok', + 'function.strtolower', + 'function.strtotime', + 'function.strtoupper', + 'function.strtr', + 'function.strval', + 'function.substr', + 'function.substr-compare', + 'function.substr-count', + 'function.substr-replace', + 'function.symlink', + 'function.sys-get-temp-dir', + 'function.sys-getloadavg', + 'function.syslog', + 'function.system', + 'function.tan', + 'function.tanh', + 'function.tempnam', + 'function.time', + 'function.time-', + 'function.timezone-', + 'function.tmpfile', + 'function.token-get-all', + 'function.token-name', + 'function.touch', + 'function.trait-exists', + 'function.trigger-error', + 'function.trim', + 'function.uasort', + 'function.ucfirst', + 'function.ucwords', + 'function.uksort', + 'function.umask', + 'function.uniqid', + 'function.unlink', + 'function.unpack', + 'function.unregister-tick-function', + 'function.unserialize', + 'function.unset', + 'function.urldecode', + 'function.urlencode', + 'function.user-error', + 'function.usleep', + 'function.usort', + 'function.var-dump', + 'function.var-export', + 'function.version-compare', + 'function.vfprintf', + 'function.vprintf', + 'function.vsprintf', + 'function.wordwrap', + 'function.zend-thread-id', + 'function.zend-version', + 'pdo.getavailabledrivers', + ], + 'cubrid' => [ + 'function.cubrid-', + ], + 'curl' => [ + 'curlfile.', + 'function.curl-', + 'function.curl_upkeep', + ], + 'dba' => [ + 'function.dba-', + ], + 'dbase' => [ + 'function.dbase-', + ], + 'dio' => [ + 'function.dio-', + ], + 'eio' => [ + 'function.eio-', + ], + 'enchant' => [ + 'function.enchant-', + ], + 'exif' => [ + 'function.exif-', + 'function.read-exif-', + ], + 'expect' => [ + 'function.expect-', + ], + 'fann' => [ + 'function.fann-', + ], + 'fdf' => [ + 'function.fdf-', + ], + 'fpm' => [ + 'function.fpm-' + ], + 'ftp' => [ + 'function.ftp-', + ], + 'gearman' => [ + 'gearmanclient.', + 'gearmantask.', + ], + 'gettext' => [ + 'function.bind-textdomain-codeset', + 'function.bindtextdomain', + 'function.dcgettext', + 'function.dcngettext', + 'function.dgettext', + 'function.dngettext', + 'function.gettext', + 'function.ngettext', + 'function.textdomain', + ], + 'gd' => [ + 'function.gd-', + 'function.getimagesize', + 'function.getimagesizefromstring', + 'function.image\.+', + 'function.iptcembed', + 'function.iptcparse', + ], + 'gmp' => [ + 'function.gmp-', + ], + 'geoip' => [ + 'function.geoip-', + ], + 'gnupg' => [ + 'function.gnupg-', + ], + 'ibase' => [ + 'function.fbird-', + 'function.ibase-', + ], + 'ibm_db2' => [ + 'function.db2-', + ], + 'iconv' => [ + 'function.iconv', + 'function.iconv-', + ], + 'igbinary' => [ + 'function.igbinary-', + ], + 'imap' => [ + 'function.imap-', + ], + 'inotify' => [ + 'function.inotify-', + ], + 'intl' => [ + 'collator.', + 'function.grapheme-', + 'function.idn-', + 'function.intl-', + 'intlcalendar.', + 'intldateformatter.', + 'intltimezone.', + 'locale.', + 'messageformatter.', + 'normalizer.', + 'numberformatter.', + 'resourcebundle.', + 'transliterator.', + ], + 'language' => [ + 'function.array', + 'function.assert', + 'function.echo', + 'function.empty', + 'function.halt-compiler', + 'function.isset', + 'function.print', + ], + 'ldap' => [ + 'function.ldap-', + ], + 'libxml' => [ + 'function.libxml-', + ], + 'lzf' => [ + 'function.lzf-', + ], + 'mailparse' => [ + 'function.mailparse-', + ], + 'mbstring' => [ + 'function.mb-', + ], + 'memcache' => [ + 'function.memcache-', + ], + 'mongodb' => [ + 'function.mongodb.', + ], + 'mqseries' => [ + 'function.mqseries-', + ], + 'mysqli' => [ + 'mysqli-', + 'mysqli.', + 'function.mysqli-', + ], + 'mysql-obsolete' => [ + 'function.mysql-', + ], + 'oauth' => [ + 'function.oauth-', + ], + 'obsolete_7' => [ + 'function.autoload', + 'function.end', + 'function.ezmlm-hash', + 'function.is-real', + 'function.jpeg2wbmp', + 'function.mcrypt-', + 'function.mdecrypt-', + 'function.png2wbmp', + 'function.wddx-', + ], + 'obsolete_8' => [ + 'function.mhash', + 'function.mhash-', + 'function.utf8-decode', + 'function.utf8-encode', + 'function.zip-', + ], + 'oci8' => [ + 'function.oci\.+', + ], + 'odbc' => [ + 'function.odbc-', + ], + 'openal' => [ + 'function.openal-', + ], + 'openssl' => [ + 'function.openssl-', + ], + 'parallel' => [ + 'parallel.', + ], + 'pcntl' => [ + 'function.pcntl-', + ], + 'pgsql' => [ + 'function.pg-', + ], + 'posix' => [ + 'function.posix-', + ], + 'ps' => [ + 'function.ps-', + ], + 'pspell' => [ + 'function.pspell-', + ], + 'radius' => [ + 'function.radius-', + ], + 'rar' => [ + 'function.rar-', + 'rararchive.', + ], + 'readline' => [ + 'function.readline', + 'function.readline-', + ], + 'recode' => [ + 'function.recode', + 'function.recode-', + ], + 'rnp' => [ + 'function.rnp-', + ], + 'runkit7' => [ + 'function.runkit7-', + ], + 'rpminfo' => [ + 'function.rpm\.+', + ], + 'rrd' => [ + 'function.rrd-', + 'function.rrdc-', + ], + 'scoutapm' => [ + 'function.scoutapm-', + ], + 'shmop' => [ + 'function.shmop-', + ], + 'simdjson' => [ + 'function.simdjson-' + ], + 'simplexml' => [ + 'function.dom-import-simplexml', + 'function.simplexml-', + ], + 'sqlsrv' => [ + 'function.sqlsrv-', + ], + 'smnp' => [ + 'function.snmp-', + 'function.snmp2-', + 'function.snmp3-', + 'function.snmp\.+', + ], + 'sodium' => [ + 'function.sodium-', + ], + 'soap' => [ + 'function.is-soap-fault', + 'function.use-soap-error-handler', + ], + 'socket' => [ + 'function.socket-', + ], + 'solr' => [ + 'function.solr-', + ], + 'ssdeep' => [ + 'function.ssdeep-', + ], + 'ssh2' => [ + 'function.ssh2-', + ], + 'stats' => [ + 'function.stats-', + ], + 'stomp' => [ + 'function.stomp-', + 'stomp.', + ], + 'svn' => [ + 'function.svn-', + ], + 'swoole' => [ + 'function.swoole-', + 'swoole-server.', + ], + 'sysvshm' => [ + 'function.ftok', + 'function.sem-', + 'function.shm-', + 'function.msg-get-queue', + 'function.msg-queue-exists', + 'function.msg-receive', + 'function.msg-remove-queue', + 'function.msg-send', + 'function.msg-set-queue', + 'function.msg-stat-queue', + ], + 'taint' => [ + 'function.is-tainted', + 'function.taint', + 'function.untaint', + ], + 'tcpwrap' => [ + 'function.tcpwrap-', + ], + 'tidy' => [ + 'function.tidy-', + 'tidy.', + ], + 'trader' => [ + 'function.trader-', + ], + 'ui' => [ + 'function.ui-', + ], + 'uopz' => [ + 'function.uopz-', + ], + 'var_representation' => [ + 'function.var-representation', + ], + 'win32service' => [ + 'function.win32-', + ], + 'wincache' => [ + 'function.wincache-', + ], + 'xattr' => [ + 'function.xattr-', + ], + 'xdiff' => [ + 'function.xdiff-', + ], + 'xhprof' => [ + 'function.xhprof-', + ], + 'xml' => [ + 'function.xml-', + ], + 'xmlrpc' => [ + 'function.xmlrpc-', + ], + 'xmlwriter' => [ + 'xmlwriter.', + ], + 'yaml' => [ + 'function.yaml-', + ], + 'yaz' => [ + 'function.yaz-', + ], + 'zlib' => [ + 'function.deflate-', + 'function.gz\.+', + 'function.inflate-', + 'function.readgzfile', + 'function.zlib-', + ], + 'zookeeper' => [ + 'function.zookeeper-', + ] +]; diff --git a/script/extract_functions.php b/script/extract_functions.php new file mode 100755 index 00000000..791c19f7 --- /dev/null +++ b/script/extract_functions.php @@ -0,0 +1,118 @@ +#!/usr/bin/env php + + * @license FSFAP https://spdx.org/licenses/FSFAP.html + */ + +// Copying and distribution of this file, with or without modification, +// are permitted in any medium without royalty provided the copyright +// notice and this notice are preserved. This file is offered as-is, +// without any warranty. + +declare(strict_types=1); + +error_reporting(E_ALL); + +$functions = json_decode(stream_get_contents(STDIN), true); + +$command = $argv[0]; +$subcommand = $argv[1] ?? null; + +$subcommands = [ + 'count' => function (array $extracted) { + echo json_encode(array_map(count(...), $extracted), JSON_PRETTY_PRINT), PHP_EOL; + }, + 'modules' => function (array $extracted) { + echo implode(PHP_EOL, array_keys($extracted)), PHP_EOL; + }, + 'functions' => function (array $extracted) { + echo json_encode(array_map(array_keys(...), $extracted), JSON_PRETTY_PRINT), PHP_EOL; + }, + 'functions-txt' => function (array $extracted) { + foreach ($extracted as $functions) { + foreach ($functions as $name => $_) { + echo $name, PHP_EOL; + } + } + }, + 'functions-sexp' => function (array $extracted) { + echo " '("; + foreach ($extracted as $module => $functions) { + echo "\n ({$module}"; + // ksort($functions); + foreach ($functions as $name => $function) { + $escaped_name = strtr($name, ['\\' => '\\\\']); + echo "\n \"{$escaped_name}\""; + } + echo ")"; + } + echo ")"; + }, +]; + +if (!isset($subcommands[$subcommand])) { + $json_url = 'http://doc.php.net/downloads/json/php_manual_en.json'; + fwrite(STDERR, implode(PHP_EOL, [ + "[Extract PHP Functions]\n", + "This script extract PHP function names from <{$json_url}>.\n", + "Usage:", + "\t{$command} count < php_manual_en.json", + "\t{$command} modules < php_manual_en.json", + "\t{$command} functions < php_manual_en.json > result.json", + "\t{$command} functions-sexp < php_manual_en.json > result.el", + '', + ])); + exit(1); +} + +$module_id_patterns = array_map( + fn($allowlist) => '/\A(?:' . implode('|', array_map( + fn($preg) => strtr($preg, ['\.' => '.', '.' => '\.']) . + (preg_match('/[0-9a-z]\z/', $preg) ? '\z' : '\b'), + $allowlist) + ) . ')/', + include __DIR__ . '/data/module_id_prefixes.php' +); + +$extracted = []; + +foreach ($functions as $name => $function) { + if (str_contains($name, '::')) { + continue; + } + + $module = get_module($function, $module_id_patterns); + + if ($module === null) { + fwrite(STDERR, "{$name}: {$function['id']}\n"); + } else { + $extracted[$module][$name] = $function; + } +} + +ksort($extracted); +array_walk($extracted, function (&$functions) { + ksort($functions); +}); + +call_user_func($subcommands[$subcommand], $extracted); + +/** + * @param array{id: non-empty-string} $function + * @param array $function + */ +function get_module(array $function, array $patterns): ?string +{ + foreach ($patterns as $module => $pattern) { + if (preg_match($pattern, $function['id'])) { + return $module; + } + } + + return null; +} diff --git a/script/module_id_prefixes.php b/script/module_id_prefixes.php new file mode 100644 index 00000000..4809a9d1 --- /dev/null +++ b/script/module_id_prefixes.php @@ -0,0 +1,806 @@ + [ + 'function.apache-', + 'function.virtual', + ], + 'apcu' => [ + 'function.apcu-', + ], + 'bcmath' => [ + 'function.bc\.+', + ], + 'bzip2' => [ + 'function.bz\.+', + ], + 'commonmark' => [ + 'function.commonmark-', + ], + 'com_dotnet' => [ + 'function.com-', + 'function.variant-', + ], + 'componere' => [ + 'componere.', + ], + 'core' => [ + 'function.abs', + 'function.acos', + 'function.acosh', + 'function.addcslashes', + 'function.addslashes', + 'function.array-', + 'function.arsort', + 'function.asin', + 'function.asinh', + 'function.asort', + 'function.atan', + 'function.atan2', + 'function.atanh', + 'function.base-convert', + 'function.base64-', + 'function.basename', + 'function.bin2hex', + 'function.bindec', + 'function.boolval', + 'function.cal-', + 'function.call-user-func', + 'function.call-user-func-array', + 'function.ceil', + 'function.chdir', + 'function.checkdate', + 'function.checkdnsrr', + 'function.chgrp', + 'function.chmod', + 'function.chop', + 'function.chown', + 'function.chr', + 'function.chroot', + 'function.chunk-split', + 'function.class-alias', + 'function.class-exists', + 'function.class-implements', + 'function.class-parents', + 'function.class-uses', + 'function.clearstatcache', + 'function.cli-', + 'function.closedir', + 'function.closelog', + 'function.compact', + 'function.connection-', + 'function.constant', + 'function.convert-cyr-string', + 'function.convert-uudecode', + 'function.convert-uuencode', + 'function.copy', + 'function.cos', + 'function.cosh', + 'function.count', + 'function.count-chars', + 'function.crc32', + 'function.create-function', + 'function.crypt', + 'function.ctype-', + 'function.current', + 'function.date', + 'function.debug-', + 'function.decbin', + 'function.dechex', + 'function.decoct', + 'function.define', + 'function.defined', + 'function.deg2rad', + 'function.delete', + 'function.die', + 'function.dir', + 'function.dirname', + 'function.disk-', + 'function.diskfreespace', + 'function.dl', + 'function.dns-check-record', + 'function.dns-get-mx', + 'function.dns-get-record', + 'function.dom-import-simplexml', + 'function.doubleval', + 'function.each', + 'function.easter-date', + 'function.easter-days', + 'function.enum-exists', + 'function.error-', + 'function.escapeshellarg', + 'function.escapeshellcmd', + 'function.eval', + 'function.exec', + 'function.exit', + 'function.exp', + 'function.explode', + 'function.expm1', + 'function.extension-loaded', + 'function.extract', + 'function.fastcgi-', + 'function.fclose', + 'function.fdatasync', + 'function.fdiv', + 'function.feof', + 'function.fflush', + 'function.fgetc', + 'function.fgetcsv', + 'function.fgets', + 'function.fgetss', + 'function.file', + 'function.file\.+', + 'function.filter-', + 'function.finfo-', + 'function.floatval', + 'function.flock', + 'function.floor', + 'function.flush', + 'function.fmod', + 'function.fnmatch', + 'function.fopen', + 'function.forward-static-call', + 'function.forward-static-call-array', + 'function.fpassthru', + 'function.fprintf', + 'function.fputcsv', + 'function.fputs', + 'function.fread', + 'function.frenchtojd', + 'function.fscanf', + 'function.fseek', + 'function.fsockopen', + 'function.fstat', + 'function.fsync', + 'function.ftell', + 'function.ftruncate', + 'function.func-get-arg', + 'function.func-get-args', + 'function.func-num-args', + 'function.function-exists', + 'function.fwrite', + 'function.gc-', + 'function.get-', + 'function.getallheaders', + 'function.getcwd', + 'function.getdate', + 'function.getenv', + 'function.gethostbyaddr', + 'function.gethostbyname', + 'function.gethostbynamel', + 'function.gethostname', + 'function.getlastmod', + 'function.getmxrr', + 'function.getmygid', + 'function.getmyinode', + 'function.getmypid', + 'function.getmyuid', + 'function.getopt', + 'function.getprotobyname', + 'function.getprotobynumber', + 'function.getrandmax', + 'function.getrusage', + 'function.getservbyname', + 'function.getservbyport', + 'function.gettimeofday', + 'function.gettype', + 'function.glob', + 'function.gmdate', + 'function.gmmktime', + 'function.gmstrftime', + 'function.gregoriantojd', + 'function.hash', + 'function.header', + 'function.header-register-callback', + 'function.header-remove', + 'function.headers-list', + 'function.headers-sent', + 'function.hebrev', + 'function.hebrevc', + 'function.hex2bin', + 'function.hexdec', + 'function.highlight-file', + 'function.highlight-string', + 'function.hrtime', + 'function.html-entity-decode', + 'function.htmlentities', + 'function.htmlspecialchars', + 'function.htmlspecialchars-decode', + 'function.http-build-query', + 'function.http-response-code', + 'function.hypot', + 'function.idate', + 'function.ignore-user-abort', + 'function.implode', + 'function.in-array', + 'function.inet-', + 'function.ini-', + 'function.intdiv', + 'function.interface-exists', + 'function.intval', + 'function.ip2long', + 'function.is-a', + 'function.is-array', + 'function.is-bool', + 'function.is-callable', + 'function.is-countable', + 'function.is-dir', + 'function.is-double', + 'function.is-executable', + 'function.is-file', + 'function.is-finite', + 'function.is-float', + 'function.is-infinite', + 'function.is-int', + 'function.is-integer', + 'function.is-iterable', + 'function.is-link', + 'function.is-long', + 'function.is-nan', + 'function.is-null', + 'function.is-numeric', + 'function.is-object', + 'function.is-readable', + 'function.is-resource', + 'function.is-scalar', + 'function.is-string', + 'function.is-subclass-of', + 'function.is-uploaded-file', + 'function.is-writable', + 'function.is-writeable', + 'function.iterator-', + 'function.jddayofweek', + 'function.jdmonthname', + 'function.jdtofrench', + 'function.jdtogregorian', + 'function.jdtojewish', + 'function.jdtojulian', + 'function.jdtounix', + 'function.jewishtojd', + 'function.join', + 'function.json-', + 'function.juliantojd', + 'function.key', + 'function.key-exists', + 'function.krsort', + 'function.ksort', + 'function.lcfirst', + 'function.lcg-value', + 'function.lchgrp', + 'function.lchown', + 'function.levenshtein', + 'function.libxml-', + 'function.link', + 'function.linkinfo', + 'function.list', + 'function.localeconv', + 'function.localtime', + 'function.log', + 'function.log10', + 'function.log1p', + 'function.long2ip', + 'function.lstat', + 'function.ltrim', + 'function.mail', + 'function.max', + 'function.md5', + 'function.md5-file', + 'function.memory-', + 'function.metaphone', + 'function.method-exists', + 'function.microtime', + 'function.mime-content-type', + 'function.min', + 'function.mkdir', + 'function.mktime', + 'function.money-format', + 'function.move-uploaded-file', + 'function.mt-', + 'function.natcasesort', + 'function.natsort', + 'function.net-get-interfaces', + 'function.next', + 'function.nl-langinfo', + 'function.nl2br', + 'function.number-format', + 'function.ob-', + 'function.octdec', + 'function.opcache-', + 'function.opendir', + 'function.openlog', + 'function.ord', + 'function.output-', + 'function.pack', + 'function.parse-ini-', + 'function.parse-str', + 'function.parse-url', + 'function.passthru', + 'function.password-', + 'function.pathinfo', + 'function.pclose', + 'function.pfsockopen', + 'function.php-ini-', + 'function.php-sapi-name', + 'function.php-strip-whitespace', + 'function.php-uname', + 'function.phpcredits', + 'function.phpdbg-', + 'function.phpinfo', + 'function.phpversion', + 'function.pi', + 'function.popen', + 'function.pos', + 'function.posix-', + 'function.pow', + 'function.preg-', + 'function.prev', + 'function.print-r', + 'function.printf', + 'function.proc-', + 'function.property-exists', + 'function.putenv', + 'function.quoted-printable-', + 'function.quotemeta', + 'function.rad2deg', + 'function.rand', + 'function.random-', + 'function.range', + 'function.rawurldecode', + 'function.rawurlencode', + 'function.readdir', + 'function.readfile', + 'function.readlink', + 'function.realpath', + 'function.register-shutdown-function', + 'function.register-tick-function', + 'function.rename', + 'function.reset', + 'function.restore-', + 'function.rewind', + 'function.rewinddir', + 'function.rmdir', + 'function.round', + 'function.rsort', + 'function.rtrim', + 'function.sapi-', + 'function.scandir', + 'function.seaslog-get-author', + 'function.seaslog-get-version', + 'function.serialize', + 'function.session-', + 'function.set-', + 'function.setcookie', + 'function.setlocale', + 'function.setrawcookie', + 'function.settype', + 'function.sha1', + 'function.sha1-file', + 'function.shell-exec', + 'function.show-source', + 'function.shuffle', + 'function.similar-text', + 'function.simplexml-', + 'function.sin', + 'function.sinh', + 'function.sizeof', + 'function.sleep', + 'function.socket-', + 'function.sodium-', + 'function.sort', + 'function.soundex', + 'function.spl-', + 'function.sprintf', + 'function.sqrt', + 'function.srand', + 'function.sscanf', + 'function.stat', + 'function.str-', + 'function.strcasecmp', + 'function.strchr', + 'function.strcmp', + 'function.strcoll', + 'function.strcspn', + 'function.stream-', + 'function.strftime', + 'function.strip-tags', + 'function.stripcslashes', + 'function.stripos', + 'function.stripslashes', + 'function.stristr', + 'function.strlen', + 'function.strnatcasecmp', + 'function.strnatcmp', + 'function.strncasecmp', + 'function.strncmp', + 'function.strpbrk', + 'function.strpos', + 'function.strptime', + 'function.strrchr', + 'function.strrev', + 'function.strripos', + 'function.strrpos', + 'function.strspn', + 'function.strstr', + 'function.strtok', + 'function.strtolower', + 'function.strtotime', + 'function.strtoupper', + 'function.strtr', + 'function.strval', + 'function.substr', + 'function.substr-compare', + 'function.substr-count', + 'function.substr-replace', + 'function.symlink', + 'function.sys-get-temp-dir', + 'function.sys-getloadavg', + 'function.syslog', + 'function.system', + 'function.tan', + 'function.tanh', + 'function.tempnam', + 'function.time', + 'function.timezone-', + 'function.tmpfile', + 'function.token-get-all', + 'function.token-name', + 'function.touch', + 'function.trait-exists', + 'function.trigger-error', + 'function.trim', + 'function.uasort', + 'function.ucfirst', + 'function.ucwords', + 'function.uksort', + 'function.umask', + 'function.uniqid', + 'function.unixtojd', + 'function.unlink', + 'function.unpack', + 'function.unregister-tick-function', + 'function.unserialize', + 'function.unset', + 'function.urldecode', + 'function.urlencode', + 'function.user-error', + 'function.usleep', + 'function.usort', + 'function.var-dump', + 'function.var-export', + 'function.version-compare', + 'function.vfprintf', + 'function.vprintf', + 'function.vsprintf', + 'function.wordwrap', + 'function.xml-', + 'function.zend-thread-id', + 'function.zend-version', + 'pdo.getavailabledrivers', + 'xmlwriter.', + ], + 'cubrid' => [ + 'function.cubrid-', + ], + 'curl' => [ + 'curlfile.', + 'function.curl-', + ], + 'dba' => [ + 'function.dba-', + ], + 'dbase' => [ + 'function.dbase-', + ], + 'dio' => [ + 'function.dio-', + ], + 'eio' => [ + 'function.eio-', + ], + 'enchant' => [ + 'function.enchant-', + ], + 'exif' => [ + 'function.exif-', + 'function.read-exif-', + ], + 'expect' => [ + 'function.expect-', + ], + 'fann' => [ + 'function.fann-', + ], + 'fdf' => [ + 'function.fdf-', + ], + 'fpm' => [ + 'function.fpm-' + ], + 'ftp' => [ + 'function.ftp-', + ], + 'gearman' => [ + 'gearmanclient.', + 'gearmantask.', + ], + 'gettext' => [ + 'function.bind-textdomain-codeset', + 'function.bindtextdomain', + 'function.dcgettext', + 'function.dcngettext', + 'function.dgettext', + 'function.dngettext', + 'function.gettext', + 'function.ngettext', + 'function.textdomain', + ], + 'gd' => [ + 'function.gd-', + 'function.getimagesize', + 'function.getimagesizefromstring', + 'function.image\.+', + 'function.iptcembed', + 'function.iptcparse', + ], + 'gmp' => [ + 'function.gmp-', + ], + 'geoip' => [ + 'function.geoip-', + ], + 'gnupg' => [ + 'function.gnupg-', + ], + 'ibase' => [ + 'function.fbird-', + 'function.ibase-', + ], + 'ibm_db2' => [ + 'function.db2-', + ], + 'iconv' => [ + 'function.iconv', + ], + 'igbinary' => [ + 'function.igbinary-', + ], + 'imap' => [ + 'function.imap-', + ], + 'inotify' => [ + 'function.inotify-', + ], + 'intl' => [ + 'collator.', + 'function.grapheme-', + 'function.idn-', + 'function.intl-', + 'intlcalendar.', + 'intldateformatter.', + 'intltimezone.', + 'locale.', + 'messageformatter.', + 'normalizer.', + 'numberformatter.', + 'resourcebundle.', + 'transliterator.', + ], + 'language' => [ + 'function.array\z', + 'function.assert', + 'function.echo', + 'function.empty', + 'function.halt-compiler', + 'function.isset', + 'function.print\z', + ], + 'ldap' => [ + 'function.ldap-', + ], + 'lzf' => [ + 'function.lzf-', + ], + 'mailparse' => [ + 'function.mailparse-', + ], + 'mbstring' => [ + 'function.mb-', + ], + 'memcache' => [ + 'function.memcache-', + ], + 'mongodb' => [ + 'function.mongodb.', + ], + 'mqseries' => [ + 'function.mqseries-', + ], + 'mysqli' => [ + 'mysqli-', + 'mysqli.', + 'function.mysqli-', + ], + 'mysql-obsolete' => [ + 'function.mysql-', + ], + 'oauth' => [ + 'function.oauth-', + ], + 'obsolete_7' => [ + 'function.autoload', + 'function.end', + 'function.ezmlm-hash', + 'function.is-real', + 'function.jpeg2wbmp', + 'function.mcrypt-', + 'function.mdecrypt-', + 'function.png2wbmp', + 'function.wddx-', + ], + 'obsolete_8' => [ + 'function.mhash', + 'function.utf8-decode', + 'function.utf8-encode', + 'function.zip-', + ], + 'oci8' => [ + 'function.oci\.+', + ], + 'odbc' => [ + 'function.odbc-', + ], + 'openal' => [ + 'function.openal-', + ], + 'openssl' => [ + 'function.openssl-', + ], + 'parallel' => [ + 'parallel.', + ], + 'pcntl' => [ + 'function.pcntl-', + ], + 'pgsql' => [ + 'function.pg-', + ], + 'ps' => [ + 'function.ps-', + ], + 'pspell' => [ + 'function.pspell-', + ], + 'radius' => [ + 'function.radius-', + ], + 'rar' => [ + 'function.rar-', + 'rararchive.', + ], + 'readline' => [ + 'function.readline', + ], + 'recode' => [ + 'function.recode', + ], + 'runkit7' => [ + 'function.runkit7-', + ], + 'rpminfo' => [ + 'function.rpm\.+', + ], + 'rrd' => [ + 'function.rrd-', + 'function.rrdc-', + ], + 'scoutapm' => [ + 'function.scoutapm-', + ], + 'shmop' => [ + 'function.shmop-', + ], + 'sqlsrv' => [ + 'function.sqlsrv-', + ], + 'smnp' => [ + 'function.snmp-', + 'function.snmp2-', + 'function.snmp3-', + 'function.snmp\.+', + ], + 'soap' => [ + 'function.is-soap-fault', + 'function.use-soap-error-handler', + ], + 'solr' => [ + 'function.solr-', + ], + 'ssdeep' => [ + 'function.ssdeep-', + ], + 'ssh2' => [ + 'function.ssh2-', + ], + 'stats' => [ + 'function.stats-', + ], + 'stomp' => [ + 'function.stomp-', + 'stomp.', + ], + 'svn' => [ + 'function.svn-', + ], + 'swoole' => [ + 'function.swoole-', + 'swoole-server.', + ], + 'sysvshm' => [ + 'function.ftok', + 'function.sem-', + 'function.shm-', + 'function.msg-get-queue', + 'function.msg-queue-exists', + 'function.msg-receive', + 'function.msg-remove-queue', + 'function.msg-send', + 'function.msg-set-queue', + 'function.msg-stat-queue', + ], + 'taint' => [ + 'function.is-tainted', + 'function.taint', + 'function.untaint', + ], + 'tcpwrap' => [ + 'function.tcpwrap-', + ], + 'tidy' => [ + 'function.tidy-', + 'tidy.', + ], + 'trader' => [ + 'function.trader-', + ], + 'ui' => [ + 'function.ui-', + ], + 'uopz' => [ + 'function.uopz-', + ], + 'var_representation' => [ + 'function.var-representation', + ], + 'win32service' => [ + 'function.win32-', + ], + 'wincache' => [ + 'function.wincache-', + ], + 'xattr' => [ + 'function.xattr-', + ], + 'xdiff' => [ + 'function.xdiff-', + ], + 'xhprof' => [ + 'function.xhprof-', + ], + 'xmlrpc' => ['function.xmlrpc-'], + 'yaml' => [ + 'function.yaml-', + ], + 'yaz' => [ + 'function.yaz-', + ], + 'zlib' => [ + 'function.deflate-', + 'function.gz\.+', + 'function.inflate-', + 'function.readgzfile', + 'function.zlib-', + ], + 'zookeeper' => [ + 'function.zookeeper-', + ] +]; diff --git a/tests/7.4/arrow-function.php.faces b/tests/7.4/arrow-function.php.faces index acfcbc5d..a944ea2c 100644 --- a/tests/7.4/arrow-function.php.faces +++ b/tests/7.4/arrow-function.php.faces @@ -1,13 +1,9 @@ ;; -*- mode: emacs-lisp -*- (("" . php-object-op) ("string" . php-property-name) (" ") @@ -48,10 +48,10 @@ (" ") ("print" . php-function-name) ("()\n {\n ") - ("var_dump" . php-function-call) + ("var_dump" . php-function-call-traditional) ("(") - ("$" . php-$this-sigil) - ("this" . php-$this) + ("$" . php-this-sigil) + ("this" . php-this) ("->" . php-object-op) ("string" . php-property-name) (");\n }\n}\n\n(") @@ -60,5 +60,5 @@ ("Typed" . font-lock-type-face) (")") ("->" . php-object-op) - ("print" . php-method-call) + ("print" . php-method-call-traditional) ("();\n")) diff --git a/tests/8.0/attribute/class.php.faces b/tests/8.0/attribute/class.php.faces index a4381657..1d249b6a 100644 --- a/tests/8.0/attribute/class.php.faces +++ b/tests/8.0/attribute/class.php.faces @@ -24,10 +24,10 @@ #[WithoutArgument] #[") - ("SingleArgument" . php-function-call) + ("SingleArgument" . php-function-call-traditional) ("(0)] #[") - ("FewArguments" . php-function-call) + ("FewArguments" . php-function-call-traditional) ("(") ("'Hello'" . php-string) (", ") @@ -40,9 +40,9 @@ ("() {} #[WithoutArgument] #[") - ("SingleArgument" . php-function-call) + ("SingleArgument" . php-function-call-traditional) ("(0)] #[") - ("FewArguments" . php-function-call) + ("FewArguments" . php-function-call-traditional) ("(") ("'Hello'" . php-string) (", ") @@ -55,11 +55,11 @@ ("() {} #[") - ("Attr2" . php-function-call) + ("Attr2" . php-function-call-traditional) ("(") ("\"foo\"" . php-string) ("), ") - ("Attr2" . php-function-call) + ("Attr2" . php-function-call-traditional) ("(") ("\"bar\"" . php-string) (")] diff --git a/tests/8.0/attribute/function.php.faces b/tests/8.0/attribute/function.php.faces index da2aea6c..00cedf07 100644 --- a/tests/8.0/attribute/function.php.faces +++ b/tests/8.0/attribute/function.php.faces @@ -11,10 +11,10 @@ #[WithoutArgument] #[") - ("SingleArgument" . php-function-call) + ("SingleArgument" . php-function-call-traditional) ("(0)] #[") - ("FewArguments" . php-function-call) + ("FewArguments" . php-function-call-traditional) ("(") ("'Hello'" . php-string) (", ") @@ -27,9 +27,9 @@ ("() {} #[WithoutArgument]#[") - ("SingleArgument" . php-function-call) + ("SingleArgument" . php-function-call-traditional) ("(0)]#[") - ("FewArguments" . php-function-call) + ("FewArguments" . php-function-call-traditional) ("(") ("'Hello'" . php-string) (", ") diff --git a/tests/8.4/property-hooks.php b/tests/8.4/property-hooks.php new file mode 100644 index 00000000..aa50f965 --- /dev/null +++ b/tests/8.4/property-hooks.php @@ -0,0 +1,33 @@ + $this->firstName . ' ' . $this->lastName; + } + + // All write operations go through this hook, and the result is what is written. + // Read access happens normally. + public string $firstName { + set => ucfirst(strtolower($value)); + } + + // All write operations go through this hook, which has to write to the backing value itself. + // Read access happens normally. + public string $lastName { + set { + if (strlen($value) < 2) { + throw new \InvalidArgumentException('Too short'); + } + $this->lastName = $value; + } + } +} + +$p = new Person(); + +$p->firstName = 'peter'; +print $p->firstName; // Prints "Peter" +$p->lastName = 'Peterson'; +print $p->fullName; // Prints "Peter Peterson" diff --git a/tests/8.4/property-hooks.php.faces b/tests/8.4/property-hooks.php.faces new file mode 100644 index 00000000..89ec60a6 --- /dev/null +++ b/tests/8.4/property-hooks.php.faces @@ -0,0 +1,146 @@ +;; -*- mode: emacs-lisp -*- +(("" . php-comparison-op) + (" ") + ("$" . php-this-sigil) + ("this" . php-this) + ("->" . php-object-op) + ("firstName" . php-property-name) + (" . ") + ("' '" . php-string) + (" . ") + ("$" . php-this-sigil) + ("this" . php-this) + ("->" . php-object-op) + ("lastName" . php-property-name) + (";\n }\n\n ") + ("// " . font-lock-comment-delimiter-face) + ("All write operations go through this hook, and the result is what is written.\n" . font-lock-comment-face) + (" ") + ("// " . font-lock-comment-delimiter-face) + ("Read access happens normally.\n" . font-lock-comment-face) + (" ") + ("public" . php-keyword) + (" ") + ("string" . php-class) + (" ") + ("$" . php-variable-sigil) + ("firstName" . php-variable-name) + (" {\n ") + ("set" . php-builtin) + (" ") + ("=" . php-assignment-op) + (">" . php-comparison-op) + (" ") + ("ucfirst" . php-function-call-traditional) + ("(") + ("strtolower" . php-function-call-traditional) + ("(") + ("$" . php-variable-sigil) + ("value" . php-variable-name) + ("));\n }\n\n ") + ("// " . font-lock-comment-delimiter-face) + ("All write operations go through this hook, which has to write to the backing value itself.\n" . font-lock-comment-face) + (" ") + ("// " . font-lock-comment-delimiter-face) + ("Read access happens normally.\n" . font-lock-comment-face) + (" ") + ("public" . php-keyword) + (" ") + ("string" . php-class) + (" ") + ("$" . php-variable-sigil) + ("lastName" . php-variable-name) + (" {\n ") + ("set" . php-builtin) + (" {\n ") + ("if" . php-keyword) + (" (") + ("strlen" . php-function-call-traditional) + ("(") + ("$" . php-variable-sigil) + ("value" . php-variable-name) + (") ") + ("<" . php-comparison-op) + (" 2) {\n ") + ("throw" . php-keyword) + (" ") + ("new" . php-keyword) + (" ") + ("\\InvalidArgumentException" . font-lock-type-face) + ("(") + ("'Too short'" . php-string) + (");\n }\n ") + ("$" . php-this-sigil) + ("this" . php-this) + ("->" . php-object-op) + ("lastName" . php-property-name) + (" ") + ("=" . php-assignment-op) + (" ") + ("$" . php-variable-sigil) + ("value" . php-variable-name) + (";\n }\n }\n}\n\n") + ("$" . php-variable-sigil) + ("p" . php-variable-name) + (" ") + ("=" . php-assignment-op) + (" ") + ("new" . php-keyword) + (" ") + ("Person" . font-lock-type-face) + ("();\n\n") + ("$" . php-variable-sigil) + ("p" . php-variable-name) + ("->" . php-object-op) + ("firstName" . php-property-name) + (" ") + ("=" . php-assignment-op) + (" ") + ("'peter'" . php-string) + (";\n") + ("print" . php-keyword) + (" ") + ("$" . php-variable-sigil) + ("p" . php-variable-name) + ("->" . php-object-op) + ("firstName" . php-property-name) + ("; ") + ("// " . font-lock-comment-delimiter-face) + ("Prints \"Peter\"\n" . font-lock-comment-face) + ("$" . php-variable-sigil) + ("p" . php-variable-name) + ("->" . php-object-op) + ("lastName" . php-property-name) + (" ") + ("=" . php-assignment-op) + (" ") + ("'Peterson'" . php-string) + (";\n") + ("print" . php-keyword) + (" ") + ("$" . php-variable-sigil) + ("p" . php-variable-name) + ("->" . php-object-op) + ("fullName" . php-property-name) + ("; ") + ("// " . font-lock-comment-delimiter-face) + ("Prints \"Peter Peterson\"\n" . font-lock-comment-face)) diff --git a/tests/constants.php.faces b/tests/constants.php.faces index 28f9523d..6718eb44 100644 --- a/tests/constants.php.faces +++ b/tests/constants.php.faces @@ -46,7 +46,7 @@ (";\n") ("SomeClass" . php-constant) ("::" . php-paamayim-nekudotayim) - ("classIdentifier" . php-static-method-call) + ("classIdentifier" . php-static-method-call-traditional) ("();\n\n") ("__halt_compiler" . php-keyword) ("();\n\n") diff --git a/tests/identifiers.php.faces b/tests/identifiers.php.faces index fbb56dfa..df098c90 100644 --- a/tests/identifiers.php.faces +++ b/tests/identifiers.php.faces @@ -43,15 +43,15 @@ ("the constant face. Just like c++-mode \"NS::Class::method()\"\n" . font-lock-comment-face) ("ClassName" . php-constant) ("::" . php-paamayim-nekudotayim) - ("method" . php-static-method-call) + ("method" . php-static-method-call-traditional) ("();\n") ("\\SpaceName\\ClassName" . php-constant) ("::" . php-paamayim-nekudotayim) - ("method" . php-static-method-call) + ("method" . php-static-method-call-traditional) ("();\n") ("\\My_Class" . php-constant) ("::" . php-paamayim-nekudotayim) - ("method" . php-static-method-call) + ("method" . php-static-method-call-traditional) ("();\n\n") ("__halt_compiler" . php-keyword) ("();\n\n") diff --git a/tests/issue-227.php b/tests/indent/issue-227.php similarity index 77% rename from tests/issue-227.php rename to tests/indent/issue-227.php index daf9200f..6f3c83f8 100644 --- a/tests/issue-227.php +++ b/tests/indent/issue-227.php @@ -8,5 +8,5 @@ function my_func() { return "a really long string with = inside " . -"some more text"; // ###php-mode-test### ((indent 49)) +"some more text"; // ###php-mode-test### ((indent 8)) } diff --git a/tests/indent/issue-623.php b/tests/indent/issue-623.php index d1059fef..833d8e4e 100644 --- a/tests/indent/issue-623.php +++ b/tests/indent/issue-623.php @@ -12,6 +12,6 @@ $arr = [ $object->something() // ###php-mode-test### ((indent 4)) - /* comment */ ->something() // ###php-mode-test### ((indent 4)) + /* comment */ ->something() // ###php-mode-test### ((indent 8)) ->something(), // ###php-mode-test### ((indent 8)) ]; // ###php-mode-test### ((indent 0)) diff --git a/tests/indent/issue-702.php b/tests/indent/issue-702.php new file mode 100644 index 00000000..3ef717e4 --- /dev/null +++ b/tests/indent/issue-702.php @@ -0,0 +1,37 @@ + PHP_VERSION_ID === 80000 + ? 'foo' + : 'bar', + true && + false, + false + || true, + 'value1' + , + 'value2' + , +]; + +var_dump( + PHP_VERSION_ID === 80000 + ? 'foo' + : 'bar', + true && // ###php-mode-test### ((indent 4)) + false, + false // ###php-mode-test### ((indent 4)) + || true, // ###php-mode-test### ((indent 8)) + // ###php-mode-test### ((indent 4)) + 1 // ###php-mode-test### ((indent 4)) + + 2 // ###php-mode-test### ((indent 8)) + / 3, // ###php-mode-test### ((indent 8)) + 'value1' // ###php-mode-test### ((indent 4)) + , // ###php-mode-test### ((indent 4)) + 'value2' // ###php-mode-test### ((indent 4)) + , // ###php-mode-test### ((indent 4)) +); diff --git a/tests/indent/issue-726.php b/tests/indent/issue-726.php new file mode 100644 index 00000000..b48c6f04 --- /dev/null +++ b/tests/indent/issue-726.php @@ -0,0 +1,13 @@ + [ + 'bee' => 2, + ], + // 'foo' => [ + // 'bar' => 1, + // ], + // ###php-mode-test### ((indent 4)) + 'lee' => 2, + // 'dee' => 3 + 'gee' => 4, // ###php-mode-test### ((indent 4)) +]; // ###php-mode-test### ((indent 0)) diff --git a/tests/indent/issue-774.php b/tests/indent/issue-774.php new file mode 100644 index 00000000..118cfc06 --- /dev/null +++ b/tests/indent/issue-774.php @@ -0,0 +1,11 @@ +someFunction("some", "parameter") // ###php-mode-test### ((indent 0)) + ->someOtherFunc(23, 42) // ###php-mode-test### ((indent 4)) + ->andAThirdFunction(); // ###php-mode-test### ((indent 4)) + +$result = DateTime::createFromFormat('Y-m-d', '2112-09-03') // ###php-mode-test### ((indent 0)) + ->someFunction(); // ###php-mode-test### ((indent 4)) + +$pages = $dbOld->createQueryBuilder() // ###php-mode-test### ((indent 0)) + ->select('*'); // ###php-mode-test### ((indent 4)) diff --git a/tests/indent/issue-793.php b/tests/indent/issue-793.php new file mode 100644 index 00000000..917666d4 --- /dev/null +++ b/tests/indent/issue-793.php @@ -0,0 +1,9 @@ + 1, // ###php-mode-test### ((indent 4)) + 'bar' => 2, + +'buz' => 3, // ###php-mode-test### ((indent 4)) +'buzbuz' => 4, // ###php-mode-test### ((indent 4)) +]; diff --git a/tests/issue-136.php.faces b/tests/issue-136.php.faces index 218fd882..0e5d6b71 100644 --- a/tests/issue-136.php.faces +++ b/tests/issue-136.php.faces @@ -11,9 +11,7 @@ ("@author" php-doc-annotation-tag font-lock-doc-face) (" USAMI Kenta \n * " . font-lock-doc-face) ("@link" php-doc-annotation-tag font-lock-doc-face) - (" " . font-lock-doc-face) - ("https://github.com/emacs-php/php-mode" link font-lock-doc-face) - ("\n * " . font-lock-doc-face) + (" https://github.com/emacs-php/php-mode\n * " . font-lock-doc-face) ("@package" php-doc-annotation-tag font-lock-doc-face) (" " . font-lock-doc-face) ("Emacs\\PHPMode" php-string font-lock-doc-face) @@ -105,8 +103,8 @@ ("$" . php-variable-sigil) ("name" . php-variable-name) (")\n {\n ") - ("$" . php-$this-sigil) - ("this" . php-$this) + ("$" . php-this-sigil) + ("this" . php-this) ("->" . php-object-op) ("name" . php-property-name) (" = ") diff --git a/tests/issue-197.php.faces b/tests/issue-197.php.faces index cd223c5b..85469d9d 100644 --- a/tests/issue-197.php.faces +++ b/tests/issue-197.php.faces @@ -1,9 +1,7 @@ ;; -*- mode: emacs-lisp -*- (("" . php-object-op) - ("int" . php-method-call) + ("int" . php-method-call-traditional) ("();\n") ("$" . php-variable-sigil) ("test" . php-variable-name) ("->" . php-object-op) - ("string" . php-method-call) + ("string" . php-method-call-traditional) ("();\n")) diff --git a/tests/issue-201.php.faces b/tests/issue-201.php.faces index 1e47bd40..5c3ccda8 100644 --- a/tests/issue-201.php.faces +++ b/tests/issue-201.php.faces @@ -1,30 +1,28 @@ ;; -*- mode: emacs-lisp -*- (("" . php-php-tag) ("\n") - ("/** GitHub Issue: " . font-lock-doc-face) - ("https://github.com/emacs-php/php-mode/issues/443" link font-lock-doc-face) - (" */" . font-lock-doc-face) + ("/** GitHub Issue: https://github.com/emacs-php/php-mode/issues/443 */" . font-lock-doc-face) ("\n") ("" . php-php-tag) ("\n") - ("/** GitHub Issue: " . font-lock-doc-face) - ("https://github.com/emacs-php/php-mode/issues/443" link font-lock-doc-face) - (" */" . font-lock-doc-face) + ("/** GitHub Issue: https://github.com/emacs-php/php-mode/issues/443 */" . font-lock-doc-face) ("\n") ("\n * " . font-lock-doc-face) ("@link" php-doc-annotation-tag font-lock-doc-face) - (" " . font-lock-doc-face) - ("https://github.com/emacs-php/php-mode" link font-lock-doc-face) - ("\n * " . font-lock-doc-face) + (" https://github.com/emacs-php/php-mode\n * " . font-lock-doc-face) ("@package" php-doc-annotation-tag font-lock-doc-face) (" " . font-lock-doc-face) ("Emacs\\PHPMode" php-string font-lock-doc-face) @@ -107,8 +105,8 @@ ("$" . php-variable-sigil) ("name" . php-variable-name) (")\n {\n ") - ("$" . php-$this-sigil) - ("this" . php-$this) + ("$" . php-this-sigil) + ("this" . php-this) ("->" . php-object-op) ("name" . php-property-name) (" ") @@ -136,7 +134,7 @@ (" ") ("=" . php-assignment-op) (" ") - ("hoge" . php-function-call) + ("hoge" . php-function-call-traditional) ("();\n\n ") ("// " . font-lock-comment-delimiter-face) ("one-line comment\n" . font-lock-comment-face) diff --git a/tests/lang/doc-comment/comments.php.27.faces b/tests/lang/doc-comment/comments.php.27.faces index e48028c8..70c23fb3 100644 --- a/tests/lang/doc-comment/comments.php.27.faces +++ b/tests/lang/doc-comment/comments.php.27.faces @@ -11,9 +11,7 @@ ("@author" php-doc-annotation-tag font-lock-doc-face) (" USAMI Kenta \n * " . font-lock-doc-face) ("@link" php-doc-annotation-tag font-lock-doc-face) - (" " . font-lock-doc-face) - ("https://github.com/emacs-php/php-mode" link font-lock-doc-face) - ("\n * " . font-lock-doc-face) + (" https://github.com/emacs-php/php-mode\n * " . font-lock-doc-face) ("@package" php-doc-annotation-tag font-lock-doc-face) (" " . font-lock-doc-face) ("Emacs\\PHPMode" php-string font-lock-doc-face) @@ -73,7 +71,7 @@ ("\n ") ("const" . php-keyword) (" ") - ("SAMPLE" . font-lock-type-face) + ("SAMPLE" . php-constant-assign) (" ") ("=" . php-assignment-op) (" ") @@ -108,8 +106,8 @@ ("$" . php-variable-sigil) ("name" . php-variable-name) (")\n {\n ") - ("$" . php-$this-sigil) - ("this" . php-$this) + ("$" . php-this-sigil) + ("this" . php-this) ("->" . php-object-op) ("name" . php-property-name) (" ") @@ -137,7 +135,7 @@ (" ") ("=" . php-assignment-op) (" ") - ("hoge" . php-function-call) + ("hoge" . php-function-call-traditional) ("();\n\n ") ("// " . font-lock-comment-delimiter-face) ("one-line comment\n" . font-lock-comment-face) diff --git a/tests/lang/doc-comment/comments.php.faces b/tests/lang/doc-comment/comments.php.faces index e6748c07..06f70485 100644 --- a/tests/lang/doc-comment/comments.php.faces +++ b/tests/lang/doc-comment/comments.php.faces @@ -11,9 +11,7 @@ ("@author" php-doc-annotation-tag font-lock-doc-face) (" USAMI Kenta \n * " . font-lock-doc-face) ("@link" php-doc-annotation-tag font-lock-doc-face) - (" " . font-lock-doc-face) - ("https://github.com/emacs-php/php-mode" link font-lock-doc-face) - ("\n * " . font-lock-doc-face) + (" https://github.com/emacs-php/php-mode\n * " . font-lock-doc-face) ("@package" php-doc-annotation-tag font-lock-doc-face) (" " . font-lock-doc-face) ("Emacs\\PHPMode" php-string font-lock-doc-face) @@ -72,7 +70,7 @@ ("\n ") ("const" . php-keyword) (" ") - ("SAMPLE" . font-lock-type-face) + ("SAMPLE" . php-constant-assign) (" ") ("=" . php-assignment-op) (" ") @@ -107,8 +105,8 @@ ("$" . php-variable-sigil) ("name" . php-variable-name) (")\n {\n ") - ("$" . php-$this-sigil) - ("this" . php-$this) + ("$" . php-this-sigil) + ("this" . php-this) ("->" . php-object-op) ("name" . php-property-name) (" ") @@ -136,7 +134,7 @@ (" ") ("=" . php-assignment-op) (" ") - ("hoge" . php-function-call) + ("hoge" . php-function-call-traditional) ("();\n\n ") ("// " . font-lock-comment-delimiter-face) ("one-line comment\n" . font-lock-comment-face) diff --git a/tests/lang/doc-comment/issue-8.php.faces b/tests/lang/doc-comment/issue-8.php.faces index 82ce4723..5a107591 100644 --- a/tests/lang/doc-comment/issue-8.php.faces +++ b/tests/lang/doc-comment/issue-8.php.faces @@ -1,9 +1,7 @@ ;; -*- mode: emacs-lisp -*- (("" . php-object-op) - ("string" . php-method-call) + ("string" . php-method-call-traditional) ("();\n") ("$" . php-variable-sigil) ("foo" . php-variable-name) ("->" . php-object-op) - ("isset" . php-method-call) + ("isset" . php-method-call-traditional) ("();\n\n\n") ("$" . php-variable-sigil) ("a" . php-variable-name) ("->" . php-object-op) - ("b" . php-method-call) + ("b" . php-method-call-traditional) ("();\n") ("$" . php-variable-sigil) ("a" . php-variable-name) (" ") ("=" . php-assignment-op) (" ") - ("a" . php-function-call) + ("a" . php-function-call-traditional) ("();\n") ("$" . php-variable-sigil) ("aaa" . php-variable-name) (" ") ("=" . php-assignment-op) (" ") - ("aaa" . php-function-call) + ("aaa" . php-function-call-traditional) ("();\n") ("$" . php-variable-sigil) ("_aa" . php-variable-name) (" ") ("=" . php-assignment-op) (" ") - ("_aa" . php-function-call) + ("_aa" . php-function-call-traditional) ("();\n") ("$" . php-variable-sigil) ("a_a" . php-variable-name) (" ") ("=" . php-assignment-op) (" ") - ("a_a" . php-function-call) + ("a_a" . php-function-call-traditional) ("();\n") ("$" . php-variable-sigil) ("aa_" . php-variable-name) (" ") ("=" . php-assignment-op) (" ") - ("aa_" . php-function-call) + ("aa_" . php-function-call-traditional) ("();\n") ("$" . php-variable-sigil) ("a1c" . php-variable-name) (" ") ("=" . php-assignment-op) (" ") - ("a1c" . php-function-call) + ("a1c" . php-function-call-traditional) ("();\n") ("$" . php-variable-sigil) ("あ" . php-variable-name) (" ") ("=" . php-assignment-op) (" ") - ("あ" . php-function-call) + ("あ" . php-function-call-traditional) ("();\n") ("$" . php-variable-sigil) ("_a" . php-variable-name) diff --git a/tests/lang/function/closure.php.faces b/tests/lang/function/closure.php.faces index 1d671f72..9bf83b50 100644 --- a/tests/lang/function/closure.php.faces +++ b/tests/lang/function/closure.php.faces @@ -1,7 +1,7 @@ ;; -*- mode: emacs-lisp -*- (("We\" is a single quote. It should not have a string face." :expected-result :failed + (skip-unless (not (eq system-type darwin))) ; TODO: Failed on macOS 28.2 or above! (with-php-mode-test ("issue-9.php") (search-forward ">We") (forward-char) ;; Jump to after the opening apostrophe @@ -517,11 +522,6 @@ style from Drupal." (search-forward "return") (should (eq (current-indentation) (* 2 c-basic-offset))))) -(ert-deftest php-mode-test-issue-227 () - "multi-line strings indents " - (custom-set-variables '(php-lineup-cascaded-calls t)) - (with-php-mode-test ("issue-227.php" :indent t :style pear :magic t))) - (ert-deftest php-mode-test-issue-237 () "Indent chaining method for PSR2." (with-php-mode-test ("issue-237.php" :indent t :style psr2 :magic t))) @@ -533,8 +533,8 @@ style from Drupal." (should (eq 'php-variable-name (get-text-property (1- (point)) 'face))) (search-forward "$this") - (should (eq 'php-$this-sigil (get-text-property (match-beginning 0) 'face))) - (should (eq 'php-$this (get-text-property (1+ (match-beginning 0)) 'face))) + (should (eq 'php-this-sigil (get-text-property (match-beginning 0) 'face))) + (should (eq 'php-this (get-text-property (1+ (match-beginning 0)) 'face))) (search-forward "$x") (should (eq 'php-variable-sigil (get-text-property (match-beginning 0) 'face))) @@ -592,7 +592,8 @@ style from Drupal." (ert-deftest php-mode-test-type-hints () "Test highlighting of type hints and return types." - (with-php-mode-test ("type-hints.php" :faces t))) + (with-php-mode-test ("type-hints.php" :faces (cond ((version<= "29" emacs-version) ".29.faces") + (t))))) (ert-deftest php-mode-test-static-method-calls () "Test highlighting of static method calls which are named the same @@ -614,6 +615,7 @@ as a keyword." (ert-deftest php-project-root () "Test for detection `php-project-root' by directory." (dolist (root (mapcar #'car php-project-available-root-files)) + (skip-unless (not (eq system-type windows-nt))) ; TODO: Make test compatible to Windows! (with-php-mode-test ("project/1/src/functions.php") (let ((php-project-root root)) (should (string= (expand-file-name "project/1/" php-mode-test-dir) @@ -642,9 +644,14 @@ Meant for `php-mode-test-issue-503'." (goto-char (point-min)) (should (eq (php-mode-test-in-function-p nil) nil)))) -(ert-deftest php-mode-test-issue-623 () - "Proper alignment object -> accessor." - (with-php-mode-test ("indent/issue-623.php" :indent t :magic t))) +(ert-deftest php-mode-test-indentation-issues () + ;; Proper alignment object -> accessor. + (with-php-mode-test ("indent/issue-623.php" :indent t :magic t)) + ;; Proper alignment arglist. + (with-php-mode-test ("indent/issue-702.php" :indent t :magic t)) + (with-php-mode-test ("indent/issue-726.php" :indent t :magic t)) + ;; Proper alignment arglist that contains empty lines. + (with-php-mode-test ("indent/issue-793.php" :indent t :magic t))) (ert-deftest php-mode-test-php74 () "Test highlighting language constructs added in PHP 7.4." @@ -662,6 +669,10 @@ Meant for `php-mode-test-issue-503'." (with-php-mode-test ("8.1/enum.php" :faces t)) (with-php-mode-test ("8.1/readonly.php" :faces t))) +(ert-deftest php-mode-test-php84 () + "Test highlighting language constructs added in PHP 8.4." + (with-php-mode-test ("8.4/property-hooks.php" :faces t))) + (ert-deftest php-mode-test-lang () "Test highlighting for language constructs." (with-php-mode-test ("lang/class/anonymous-class.php" :indent t :magic t :faces t)) @@ -675,6 +686,8 @@ Meant for `php-mode-test-issue-503'." (with-php-mode-test ("lang/doc-comment/return-type.php" :faces t)) (with-php-mode-test ("lang/function/calls.php" :faces t)) (with-php-mode-test ("lang/function/closure.php" :indent t :magic t :faces t)) + (with-php-mode-test ("lang/import/import-constant.php" :faces t)) + (with-php-mode-test ("lang/import/import-function.php" :faces t)) (with-php-mode-test ("lang/try-cactch/multiple.php" :faces t)) (with-php-mode-test ("lang/types/cast.php" :faces t)) (with-php-mode-test ("lang/types/function.php" :faces t)) @@ -682,6 +695,11 @@ Meant for `php-mode-test-issue-503'." (with-php-mode-test ("lang/errorcontrol.php" :faces t)) (with-php-mode-test ("lang/magical-constants/echo.php" :faces t))) +(ert-deftest php-mode-test-pear () + "Tests for PEAR style." + (with-php-mode-test ("indent/issue-227.php" :indent t :magic t :style pear)) + (with-php-mode-test ("indent/issue-774.php" :indent t :magic t :style pear))) + ;; For developers: How to make .faces list file. ;; ;; 1. Press `M-x eval-buffer' in this file bufffer. diff --git a/tests/static-method-calls.php.faces b/tests/static-method-calls.php.faces index 34e77b91..8cdd0815 100644 --- a/tests/static-method-calls.php.faces +++ b/tests/static-method-calls.php.faces @@ -3,37 +3,37 @@ ("\n\n") ("ClassName" . php-constant) ("::" . php-paamayim-nekudotayim) - ("method" . php-static-method-call) + ("method" . php-static-method-call-traditional) ("();\n") ("\\Project\\Module\\ClassName" . php-constant) ("::" . php-paamayim-nekudotayim) - ("method" . php-static-method-call) + ("method" . php-static-method-call-traditional) ("();\n") ("\\ClassName" . php-constant) ("::" . php-paamayim-nekudotayim) - ("method" . php-static-method-call) + ("method" . php-static-method-call-traditional) ("();\n\n") ("ClassName" . php-constant) ("::" . php-paamayim-nekudotayim) - ("new" . php-static-method-call) + ("new" . php-static-method-call-traditional) ("();\n") ("\\Project\\Module\\ClassName" . php-constant) ("::" . php-paamayim-nekudotayim) - ("new" . php-static-method-call) + ("new" . php-static-method-call-traditional) ("();\n") ("\\ClassName" . php-constant) ("::" . php-paamayim-nekudotayim) - ("new" . php-static-method-call) + ("new" . php-static-method-call-traditional) ("();\n\n") ("ClassName" . php-constant) ("::" . php-paamayim-nekudotayim) - ("clone" . php-static-method-call) + ("clone" . php-static-method-call-traditional) ("();\n") ("\\Project\\Module\\ClassName" . php-constant) ("::" . php-paamayim-nekudotayim) - ("clone" . php-static-method-call) + ("clone" . php-static-method-call-traditional) ("();\n") ("\\ClassName" . php-constant) ("::" . php-paamayim-nekudotayim) - ("clone" . php-static-method-call) + ("clone" . php-static-method-call-traditional) ("();\n")) diff --git a/tests/type-hints.php.29.faces b/tests/type-hints.php.29.faces new file mode 100644 index 00000000..14cf581e --- /dev/null +++ b/tests/type-hints.php.29.faces @@ -0,0 +1,590 @@ +;; -*- mode: emacs-lisp -*- +(("" . php-object-op) - ("funCall" . php-method-call) + ("funCall" . php-method-call-traditional) ("();\n"))