@markuplint/php-parser 4.6.21 → 4.6.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.ja.md +87 -0
- package/ARCHITECTURE.md +87 -0
- package/CHANGELOG.md +5 -1
- package/SKILL.md +54 -0
- package/docs/maintenance.ja.md +105 -0
- package/docs/maintenance.md +105 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.js +6 -0
- package/lib/parser.d.ts +11 -0
- package/lib/parser.js +11 -0
- package/package.json +4 -4
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# @markuplint/php-parser
|
|
2
|
+
|
|
3
|
+
## 概要
|
|
4
|
+
|
|
5
|
+
`@markuplint/php-parser` は `HtmlParser` を拡張し、PHP コードブロックを含む HTML のリントを可能にします。すべての PHP タグバリアントを不透明ブロックとして扱い、markuplint が PHP 構文に影響されることなく周囲の HTML 構造をリントできるようにします。
|
|
6
|
+
|
|
7
|
+
## 動作の仕組み
|
|
8
|
+
|
|
9
|
+
このパーサーは基底の `HtmlParser` が提供する `ignoreTags` メカニズムを使用します:
|
|
10
|
+
|
|
11
|
+
1. **マスク** — パース前に、すべての PHP タグ式(`<?php ... ?>`、`<?= ... ?>`、`<? ... ?>`)が開始/終了デリミタにより識別され、プレースホルダーテキストに置換される
|
|
12
|
+
2. **パース** — マスクされた HTML は、PHP 式が存在しないかのように標準 HTML パーサー(parse5)によってパースされる
|
|
13
|
+
3. **保持** — 元の PHP 式は AST 内に `#ps:*`(PreprocessorSpecificBlock)ノードとして保持され、ソース位置も維持される
|
|
14
|
+
|
|
15
|
+
このアプローチにより、markuplint は PHP 構文に影響されることなく HTML 構造をリントできます。
|
|
16
|
+
|
|
17
|
+
## ignoreTags 設定
|
|
18
|
+
|
|
19
|
+
`PHPParser` コンストラクタは、正しいマッチングを保証するために、最も具体的なものから順に3つのパターンを定義しています:
|
|
20
|
+
|
|
21
|
+
| タイプ | 開始 | 終了 | 説明 |
|
|
22
|
+
| --------------- | ------- | ----- | ------------------------- | ----------------------------------------------------- |
|
|
23
|
+
| `php-tag` | `<?php` | `/\?> | $/` | 標準 PHP コードブロック(EOF で未閉鎖のタグにも対応) |
|
|
24
|
+
| `php-echo` | `<?=` | `?>` | ショートエコー / 出力タグ |
|
|
25
|
+
| `php-short-tag` | `<?` | `/\?> | $/` | ショートオープンタグ(EOF で未閉鎖のタグにも対応) |
|
|
26
|
+
|
|
27
|
+
**EOF 未閉鎖タグの処理:** `php-tag` と `php-short-tag` パターンは終了デリミタに正規表現 `/\?>|$/` を使用しています。`$` はソースの末尾にマッチし、閉じられていない PHP ブロック(例: ファイル末尾の `<?php include("path/to")`)を単一の `#ps:*` ノードとして正しくキャプチャします。
|
|
28
|
+
|
|
29
|
+
`php-echo` パターンはプレーン文字列 `?>` を使用しています。エコータグはテンプレート内で常に閉じられることが想定されているためです。
|
|
30
|
+
|
|
31
|
+
## サポートされない構文
|
|
32
|
+
|
|
33
|
+
**引用符なしの属性値**内のテンプレート式はサポートされていません。これはすべてのテンプレートエンジンパーサーに共通する既知の制限です([#240](https://github.com/markuplint/markuplint/issues/240))。[ウェブサイトのドキュメント](https://markuplint.dev/docs/guides/besides-html)も参照してください。
|
|
34
|
+
|
|
35
|
+
使用可能:
|
|
36
|
+
|
|
37
|
+
```html
|
|
38
|
+
<div attr="<?php echo value; ?>"></div>
|
|
39
|
+
<div attr="<?php echo value; ?>"></div>
|
|
40
|
+
<div attr="<?php echo value; ?>-<?php echo value2; ?>-<?php echo value3; ?>"></div>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
使用不可(引用符なし):
|
|
44
|
+
|
|
45
|
+
```html
|
|
46
|
+
<div attr=<?php echo value; ?>></div>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## ディレクトリ構成
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
src/
|
|
53
|
+
├── index.ts — parser を再エクスポート
|
|
54
|
+
├── parser.ts — HtmlParser を拡張する PHPParser クラス
|
|
55
|
+
└── index.spec.ts — パーサー統合テスト
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## 主要ソースファイル
|
|
59
|
+
|
|
60
|
+
| ファイル | 用途 |
|
|
61
|
+
| --------------- | ---------------------------------------------------------------- |
|
|
62
|
+
| `src/parser.ts` | `PHPParser` クラスを定義し、シングルトン `parser` をエクスポート |
|
|
63
|
+
| `src/index.ts` | パッケージエントリーポイント。`parser` を再エクスポート |
|
|
64
|
+
|
|
65
|
+
## 統合ポイント
|
|
66
|
+
|
|
67
|
+
```mermaid
|
|
68
|
+
flowchart TD
|
|
69
|
+
subgraph upstream ["上流"]
|
|
70
|
+
htmlParser["@markuplint/html-parser\n(HtmlParser クラス)"]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
subgraph pkg ["@markuplint/php-parser"]
|
|
74
|
+
phpParser["PHPParser\nextends HtmlParser\n(ignoreTags のみ)"]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
subgraph downstream ["下流"]
|
|
78
|
+
mlCore["@markuplint/ml-core\n(MLASTDocument → MLDOM)"]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
htmlParser -->|"継承"| phpParser
|
|
82
|
+
phpParser -->|"MLASTDocument を生成"| mlCore
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## ドキュメントマップ
|
|
86
|
+
|
|
87
|
+
- [メンテナンスガイド](docs/maintenance.ja.md) — コマンド、レシピ、テスト
|
package/ARCHITECTURE.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# @markuplint/php-parser
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
`@markuplint/php-parser` extends `HtmlParser` to lint HTML containing PHP code blocks. It treats all PHP tag variants as opaque blocks, allowing markuplint to lint the surrounding HTML structure without being confused by PHP syntax.
|
|
6
|
+
|
|
7
|
+
## How It Works
|
|
8
|
+
|
|
9
|
+
The parser uses the `ignoreTags` mechanism provided by the base `HtmlParser`:
|
|
10
|
+
|
|
11
|
+
1. **Mask** — Before parsing, all PHP tag expressions (`<?php ... ?>`, `<?= ... ?>`, `<? ... ?>`) are identified by their start/end delimiters and replaced with placeholder text
|
|
12
|
+
2. **Parse** — The masked HTML is parsed by the standard HTML parser (parse5) as if the PHP expressions did not exist
|
|
13
|
+
3. **Preserve** — The original PHP expressions are preserved in the AST as `#ps:*` (PreprocessorSpecificBlock) nodes, maintaining their source positions
|
|
14
|
+
|
|
15
|
+
This approach allows markuplint to lint the HTML structure without being confused by PHP syntax.
|
|
16
|
+
|
|
17
|
+
## ignoreTags Configuration
|
|
18
|
+
|
|
19
|
+
The `PHPParser` constructor defines three ignore patterns, ordered from most specific to least specific to ensure correct matching:
|
|
20
|
+
|
|
21
|
+
| Type | Start | End | Description |
|
|
22
|
+
| --------------- | ------- | ----- | ------------------------ | ------------------------------------------------------- |
|
|
23
|
+
| `php-tag` | `<?php` | `/\?> | $/` | Standard PHP code blocks (also matches unclosed at EOF) |
|
|
24
|
+
| `php-echo` | `<?=` | `?>` | Short echo / output tags |
|
|
25
|
+
| `php-short-tag` | `<?` | `/\?> | $/` | Short open tags (also matches unclosed at EOF) |
|
|
26
|
+
|
|
27
|
+
**EOF-unclosed tag handling:** The `php-tag` and `php-short-tag` patterns use a regex `/\?>|$/` for the end delimiter. The `$` alternative matches the end of the source, allowing PHP blocks that are never closed (e.g., `<?php include("path/to")` at the end of a file) to be correctly captured as a single `#ps:*` node rather than leaving unparsed content.
|
|
28
|
+
|
|
29
|
+
The `php-echo` pattern uses a plain string `?>` because echo tags are always expected to be closed within the template.
|
|
30
|
+
|
|
31
|
+
## Unsupported Syntaxes
|
|
32
|
+
|
|
33
|
+
Template expressions inside **unquoted attribute values** are not supported. This is a known limitation shared by all template engine parsers ([#240](https://github.com/markuplint/markuplint/issues/240)). See also the [website documentation](https://markuplint.dev/docs/guides/besides-html).
|
|
34
|
+
|
|
35
|
+
Available:
|
|
36
|
+
|
|
37
|
+
```html
|
|
38
|
+
<div attr="<?php echo value; ?>"></div>
|
|
39
|
+
<div attr="<?php echo value; ?>"></div>
|
|
40
|
+
<div attr="<?php echo value; ?>-<?php echo value2; ?>-<?php echo value3; ?>"></div>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Unavailable (unquoted):
|
|
44
|
+
|
|
45
|
+
```html
|
|
46
|
+
<div attr=<?php echo value; ?>></div>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Directory Structure
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
src/
|
|
53
|
+
├── index.ts — Re-exports parser
|
|
54
|
+
├── parser.ts — PHPParser class extending HtmlParser
|
|
55
|
+
└── index.spec.ts — Parser integration tests
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Key Source Files
|
|
59
|
+
|
|
60
|
+
| File | Purpose |
|
|
61
|
+
| --------------- | ----------------------------------------------------------------- |
|
|
62
|
+
| `src/parser.ts` | Defines `PHPParser` class and exports singleton `parser` instance |
|
|
63
|
+
| `src/index.ts` | Package entry point; re-exports `parser` |
|
|
64
|
+
|
|
65
|
+
## Integration Points
|
|
66
|
+
|
|
67
|
+
```mermaid
|
|
68
|
+
flowchart TD
|
|
69
|
+
subgraph upstream ["Upstream"]
|
|
70
|
+
htmlParser["@markuplint/html-parser\n(HtmlParser class)"]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
subgraph pkg ["@markuplint/php-parser"]
|
|
74
|
+
phpParser["PHPParser\nextends HtmlParser\n(ignoreTags only)"]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
subgraph downstream ["Downstream"]
|
|
78
|
+
mlCore["@markuplint/ml-core\n(MLASTDocument → MLDOM)"]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
htmlParser -->|"extends"| phpParser
|
|
82
|
+
phpParser -->|"produces MLASTDocument"| mlCore
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Documentation Map
|
|
86
|
+
|
|
87
|
+
- [Maintenance Guide](docs/maintenance.md) — Commands, recipes, and testing
|
package/CHANGELOG.md
CHANGED
|
@@ -3,13 +3,17 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
-
## [4.6.
|
|
6
|
+
## [4.6.23](https://github.com/markuplint/markuplint/compare/@markuplint/php-parser@4.6.22...@markuplint/php-parser@4.6.23) (2026-02-10)
|
|
7
7
|
|
|
8
8
|
**Note:** Version bump only for package @markuplint/php-parser
|
|
9
9
|
|
|
10
|
+
## [4.6.22](https://github.com/markuplint/markuplint/compare/@markuplint/php-parser@4.6.21...@markuplint/php-parser@4.6.22) (2025-11-05)
|
|
10
11
|
|
|
12
|
+
**Note:** Version bump only for package @markuplint/php-parser
|
|
11
13
|
|
|
14
|
+
## [4.6.21](https://github.com/markuplint/markuplint/compare/@markuplint/php-parser@4.6.20...@markuplint/php-parser@4.6.21) (2025-08-24)
|
|
12
15
|
|
|
16
|
+
**Note:** Version bump only for package @markuplint/php-parser
|
|
13
17
|
|
|
14
18
|
## [4.6.20](https://github.com/markuplint/markuplint/compare/@markuplint/php-parser@4.6.19...@markuplint/php-parser@4.6.20) (2025-08-13)
|
|
15
19
|
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Perform maintenance tasks for @markuplint/php-parser
|
|
3
|
+
globs:
|
|
4
|
+
- packages/@markuplint/php-parser/src/**
|
|
5
|
+
alwaysApply: false
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# @markuplint/php-parser Maintenance
|
|
9
|
+
|
|
10
|
+
You are maintaining `@markuplint/php-parser`, the PHP template parser for markuplint.
|
|
11
|
+
|
|
12
|
+
## Architecture
|
|
13
|
+
|
|
14
|
+
See [ARCHITECTURE.md](ARCHITECTURE.md) for the full architecture overview including the ignoreTags mechanism and integration points.
|
|
15
|
+
|
|
16
|
+
For detailed maintenance procedures, see [docs/maintenance.md](docs/maintenance.md) ([Japanese](docs/maintenance.ja.md)).
|
|
17
|
+
|
|
18
|
+
## Key Files
|
|
19
|
+
|
|
20
|
+
| File | Role |
|
|
21
|
+
| --------------- | --------------------------------------------- |
|
|
22
|
+
| `src/parser.ts` | PHPParser class with ignoreTags configuration |
|
|
23
|
+
| `src/index.ts` | Package entry point; re-exports parser |
|
|
24
|
+
|
|
25
|
+
## Tasks
|
|
26
|
+
|
|
27
|
+
### add-ignore-tag
|
|
28
|
+
|
|
29
|
+
Add a new PHP tag variant to the ignoreTags configuration.
|
|
30
|
+
|
|
31
|
+
1. Open `src/parser.ts`
|
|
32
|
+
2. Add a new entry to the `ignoreTags` array in the `PHPParser` constructor
|
|
33
|
+
- Place it **before** `php-short-tag` (the most generic pattern must remain last)
|
|
34
|
+
- Use a string for the `start` delimiter if it is a fixed prefix
|
|
35
|
+
- Use `/\?>|$/` for `end` if the tag may be unclosed at EOF; use `?>` if the tag is always closed
|
|
36
|
+
3. Add a test case in `src/index.spec.ts` under the `Tags` describe block:
|
|
37
|
+
```ts
|
|
38
|
+
test('new-type-name', () => {
|
|
39
|
+
expect(parse('<new-delimiter any ?>').nodeList[0]?.nodeName).toBe('#ps:new-type-name');
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
4. Build: `yarn build --scope @markuplint/php-parser`
|
|
43
|
+
5. Test: `yarn test --scope @markuplint/php-parser`
|
|
44
|
+
|
|
45
|
+
### modify-ignore-tag
|
|
46
|
+
|
|
47
|
+
Modify an existing PHP tag pattern (start/end delimiter or type name).
|
|
48
|
+
|
|
49
|
+
1. Open `src/parser.ts`
|
|
50
|
+
2. Find the target entry in the `ignoreTags` array and update `type`, `start`, or `end`
|
|
51
|
+
3. Update affected test cases in `src/index.spec.ts`
|
|
52
|
+
- Check both `Tags` tests (nodeName assertions) and `Node list` tests (debug map snapshots)
|
|
53
|
+
4. Build: `yarn build --scope @markuplint/php-parser`
|
|
54
|
+
5. Test: `yarn test --scope @markuplint/php-parser`
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# メンテナンスガイド
|
|
2
|
+
|
|
3
|
+
## コマンド
|
|
4
|
+
|
|
5
|
+
| コマンド | 説明 |
|
|
6
|
+
| ------------------------------------------- | ---------------------- |
|
|
7
|
+
| `yarn build --scope @markuplint/php-parser` | このパッケージをビルド |
|
|
8
|
+
| `yarn dev --scope @markuplint/php-parser` | ウォッチモードでビルド |
|
|
9
|
+
| `yarn clean --scope @markuplint/php-parser` | ビルド成果物を削除 |
|
|
10
|
+
| `yarn test --scope @markuplint/php-parser` | テストを実行 |
|
|
11
|
+
|
|
12
|
+
## テスト
|
|
13
|
+
|
|
14
|
+
テストファイルは `*.spec.ts` の命名規則に従い、`src/` ディレクトリに配置されています:
|
|
15
|
+
|
|
16
|
+
| テストファイル | カバレッジ |
|
|
17
|
+
| --------------- | ---------------------------------------------------------------- |
|
|
18
|
+
| `index.spec.ts` | PHPParser 統合テスト(エコータグ、ショートタグ、EOF 未閉鎖タグ) |
|
|
19
|
+
|
|
20
|
+
主なテストパターンでは `nodeListToDebugMaps` を使用したスナップショット形式のアサーションを行います:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { nodeListToDebugMaps } from '@markuplint/parser-utils';
|
|
24
|
+
import { parser } from './parser.js';
|
|
25
|
+
|
|
26
|
+
const doc = parser.parse('<div><?= name ?></div>');
|
|
27
|
+
const debugMaps = nodeListToDebugMaps(doc.nodeList);
|
|
28
|
+
expect(debugMaps).toStrictEqual([
|
|
29
|
+
// 期待されるデバッグ出力
|
|
30
|
+
]);
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### タグタイプのアサーション
|
|
34
|
+
|
|
35
|
+
各 PHP タグバリアントには `#ps:*` ノード名を検証する専用テストがあります:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
expect(parse('<?php any; ?>').nodeList[0]?.nodeName).toBe('#ps:php-tag');
|
|
39
|
+
expect(parse('<?= any ?>').nodeList[0]?.nodeName).toBe('#ps:php-echo');
|
|
40
|
+
expect(parse('<? any; ?>').nodeList[0]?.nodeName).toBe('#ps:php-short-tag');
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### EOF 未閉鎖タグのテスト
|
|
44
|
+
|
|
45
|
+
テストスイートでは、閉じ `?>` がない PHP タグが正しくキャプチャされることを検証しています:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
expect(parse('<?php any;').nodeList[0]?.nodeName).toBe('#ps:php-tag');
|
|
49
|
+
expect(parse('<? any;').nodeList[0]?.nodeName).toBe('#ps:php-short-tag');
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## レシピ
|
|
53
|
+
|
|
54
|
+
### 1. 新しい PHP タグバリアントの追加
|
|
55
|
+
|
|
56
|
+
1. `src/parser.ts` を開く
|
|
57
|
+
2. `ignoreTags` 配列に新しいエントリを追加:
|
|
58
|
+
- `php-short-tag` の**前**に配置する(最も汎用的な `<?` パターンは最後に残す必要あり)
|
|
59
|
+
- `start` にはデリミタが固定プレフィックスの場合は文字列を使用
|
|
60
|
+
- `end` にはタグが EOF で未閉鎖のまま残る可能性がある場合は `/\?>|$/` を、常に閉じられる場合は `?>` を使用
|
|
61
|
+
3. `src/index.spec.ts` にテストケースを追加:
|
|
62
|
+
- `Tags` テストで `#ps:*` ノード名を検証
|
|
63
|
+
- `Node list` テストで周囲の HTML を含むデバッグマップ出力を検証
|
|
64
|
+
4. ビルド: `yarn build --scope @markuplint/php-parser`
|
|
65
|
+
5. テスト: `yarn test --scope @markuplint/php-parser`
|
|
66
|
+
|
|
67
|
+
### 2. 既存タグパターンの変更
|
|
68
|
+
|
|
69
|
+
1. `src/parser.ts` を開く
|
|
70
|
+
2. `ignoreTags` 配列内の対象エントリを見つけ、`type`、`start`、または `end` を更新
|
|
71
|
+
3. `src/index.spec.ts` の影響を受けるテストケースを更新:
|
|
72
|
+
- `Tags` テスト(nodeName アサーション)と `Node list` テスト(デバッグマップスナップショット)の両方を確認
|
|
73
|
+
4. ビルド: `yarn build --scope @markuplint/php-parser`
|
|
74
|
+
5. テスト: `yarn test --scope @markuplint/php-parser`
|
|
75
|
+
|
|
76
|
+
### 3. 上流 HtmlParser 依存の更新
|
|
77
|
+
|
|
78
|
+
1. `package.json` の `@markuplint/html-parser` 依存を更新
|
|
79
|
+
2. ビルド: `yarn build --scope @markuplint/php-parser`
|
|
80
|
+
3. テスト: `yarn test --scope @markuplint/php-parser`
|
|
81
|
+
4. テストが失敗した場合は `HtmlParser` の変更履歴で `ignoreTags` メカニズムの破壊的変更を確認
|
|
82
|
+
|
|
83
|
+
## トラブルシューティング
|
|
84
|
+
|
|
85
|
+
### PHP タグが認識されない
|
|
86
|
+
|
|
87
|
+
**症状:** PHP タグが `#ps:*` ノードではなく、AST 内で生テキストとして表示される。
|
|
88
|
+
|
|
89
|
+
**原因:** `start` デリミタが入力とマッチしない、またはより具体的なパターンが先にマッチした。
|
|
90
|
+
|
|
91
|
+
**解決策:**
|
|
92
|
+
|
|
93
|
+
1. `ignoreTags` の順序を確認 — より具体的なパターン(例: `<?php`)は、より汎用的なパターン(例: `<?`)の前に配置する必要あり
|
|
94
|
+
2. `start` 文字列が入力内の正確な文字とマッチすることを確認
|
|
95
|
+
|
|
96
|
+
### 未閉鎖の PHP タグがファイルの残り全体を消費する
|
|
97
|
+
|
|
98
|
+
**症状:** `?>` で閉じられるべき PHP タグが、代わりにファイルの末尾まで拡張される。
|
|
99
|
+
|
|
100
|
+
**原因:** `end` パターンが `/\?>|$/` を使用しており、`?>` が正しくマッチされていない。
|
|
101
|
+
|
|
102
|
+
**解決策:**
|
|
103
|
+
|
|
104
|
+
1. `?>` が PHP の文字列リテラルやコメント内にないことを確認(このパーサーは PHP 構文を解析しない — デリミタのマッチングのみ行う)
|
|
105
|
+
2. `end` 正規表現が正しいことを確認: `/\?>|$/`(`\?` はエスケープが必要)
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Maintenance Guide
|
|
2
|
+
|
|
3
|
+
## Commands
|
|
4
|
+
|
|
5
|
+
| Command | Description |
|
|
6
|
+
| ------------------------------------------- | ---------------------- |
|
|
7
|
+
| `yarn build --scope @markuplint/php-parser` | Build this package |
|
|
8
|
+
| `yarn dev --scope @markuplint/php-parser` | Watch mode build |
|
|
9
|
+
| `yarn clean --scope @markuplint/php-parser` | Remove build artifacts |
|
|
10
|
+
| `yarn test --scope @markuplint/php-parser` | Run tests |
|
|
11
|
+
|
|
12
|
+
## Testing
|
|
13
|
+
|
|
14
|
+
Test files follow the `*.spec.ts` naming convention and are located in the `src/` directory:
|
|
15
|
+
|
|
16
|
+
| Test File | Coverage |
|
|
17
|
+
| --------------- | ------------------------------------------------------------------------- |
|
|
18
|
+
| `index.spec.ts` | PHPParser integration tests (echo tags, short tags, unclosed tags at EOF) |
|
|
19
|
+
|
|
20
|
+
The primary testing pattern uses `nodeListToDebugMaps` for snapshot-style assertions:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { nodeListToDebugMaps } from '@markuplint/parser-utils';
|
|
24
|
+
import { parser } from './parser.js';
|
|
25
|
+
|
|
26
|
+
const doc = parser.parse('<div><?= name ?></div>');
|
|
27
|
+
const debugMaps = nodeListToDebugMaps(doc.nodeList);
|
|
28
|
+
expect(debugMaps).toStrictEqual([
|
|
29
|
+
// expected debug output
|
|
30
|
+
]);
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Tag type assertions
|
|
34
|
+
|
|
35
|
+
Each PHP tag variant has a dedicated test verifying its `#ps:*` node name:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
expect(parse('<?php any; ?>').nodeList[0]?.nodeName).toBe('#ps:php-tag');
|
|
39
|
+
expect(parse('<?= any ?>').nodeList[0]?.nodeName).toBe('#ps:php-echo');
|
|
40
|
+
expect(parse('<? any; ?>').nodeList[0]?.nodeName).toBe('#ps:php-short-tag');
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### EOF-unclosed tag tests
|
|
44
|
+
|
|
45
|
+
The test suite verifies that PHP tags without a closing `?>` are correctly captured:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
expect(parse('<?php any;').nodeList[0]?.nodeName).toBe('#ps:php-tag');
|
|
49
|
+
expect(parse('<? any;').nodeList[0]?.nodeName).toBe('#ps:php-short-tag');
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Recipes
|
|
53
|
+
|
|
54
|
+
### 1. Adding a New PHP Tag Variant
|
|
55
|
+
|
|
56
|
+
1. Open `src/parser.ts`
|
|
57
|
+
2. Add a new entry to the `ignoreTags` array:
|
|
58
|
+
- Place it **before** `php-short-tag` (the most generic `<?` pattern must remain last)
|
|
59
|
+
- Use a string for `start` if the delimiter is a fixed prefix
|
|
60
|
+
- Use `/\?>|$/` for `end` if the tag may remain unclosed at EOF; use `?>` if the tag is always closed
|
|
61
|
+
3. Add test cases in `src/index.spec.ts`:
|
|
62
|
+
- A `Tags` test verifying the `#ps:*` node name
|
|
63
|
+
- A `Node list` test verifying the debug map output with surrounding HTML
|
|
64
|
+
4. Build: `yarn build --scope @markuplint/php-parser`
|
|
65
|
+
5. Test: `yarn test --scope @markuplint/php-parser`
|
|
66
|
+
|
|
67
|
+
### 2. Modifying an Existing Tag Pattern
|
|
68
|
+
|
|
69
|
+
1. Open `src/parser.ts`
|
|
70
|
+
2. Find the target entry in the `ignoreTags` array and update `type`, `start`, or `end`
|
|
71
|
+
3. Update affected test cases in `src/index.spec.ts`:
|
|
72
|
+
- Check both `Tags` tests (nodeName assertions) and `Node list` tests (debug map snapshots)
|
|
73
|
+
4. Build: `yarn build --scope @markuplint/php-parser`
|
|
74
|
+
5. Test: `yarn test --scope @markuplint/php-parser`
|
|
75
|
+
|
|
76
|
+
### 3. Updating the Upstream HtmlParser Dependency
|
|
77
|
+
|
|
78
|
+
1. Update the `@markuplint/html-parser` dependency in `package.json`
|
|
79
|
+
2. Build: `yarn build --scope @markuplint/php-parser`
|
|
80
|
+
3. Test: `yarn test --scope @markuplint/php-parser`
|
|
81
|
+
4. If tests fail, check the `HtmlParser` changelog for breaking changes in the `ignoreTags` mechanism
|
|
82
|
+
|
|
83
|
+
## Troubleshooting
|
|
84
|
+
|
|
85
|
+
### PHP tag is not recognized
|
|
86
|
+
|
|
87
|
+
**Symptom:** A PHP tag appears as raw text in the AST instead of a `#ps:*` node.
|
|
88
|
+
|
|
89
|
+
**Cause:** The `start` delimiter does not match the input, or a more specific pattern matched first.
|
|
90
|
+
|
|
91
|
+
**Solution:**
|
|
92
|
+
|
|
93
|
+
1. Check the ordering of `ignoreTags` — more specific patterns (e.g., `<?php`) must appear before less specific ones (e.g., `<?`)
|
|
94
|
+
2. Verify the `start` string matches the exact characters in the input
|
|
95
|
+
|
|
96
|
+
### Unclosed PHP tag consumes the rest of the file
|
|
97
|
+
|
|
98
|
+
**Symptom:** A PHP tag that should be closed by `?>` instead extends to the end of the file.
|
|
99
|
+
|
|
100
|
+
**Cause:** The `end` pattern uses `/\?>|$/` and the `?>` is not being matched correctly.
|
|
101
|
+
|
|
102
|
+
**Solution:**
|
|
103
|
+
|
|
104
|
+
1. Verify the `?>` is not inside a PHP string literal or comment (this parser does not analyze PHP syntax — it only matches delimiters)
|
|
105
|
+
2. Check that the `end` regex is correct: `/\?>|$/` (the `\?` must be escaped)
|
package/lib/index.d.ts
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @markuplint/php-parser
|
|
3
|
+
* Markuplint parser plugin for PHP templates. Extends the standard HTML parser
|
|
4
|
+
* to treat PHP code blocks and short echo tags as opaque blocks, allowing
|
|
5
|
+
* markuplint to lint the surrounding HTML structure.
|
|
6
|
+
*/
|
|
1
7
|
export { parser } from './parser.js';
|
package/lib/index.js
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @markuplint/php-parser
|
|
3
|
+
* Markuplint parser plugin for PHP templates. Extends the standard HTML parser
|
|
4
|
+
* to treat PHP code blocks and short echo tags as opaque blocks, allowing
|
|
5
|
+
* markuplint to lint the surrounding HTML structure.
|
|
6
|
+
*/
|
|
1
7
|
export { parser } from './parser.js';
|
package/lib/parser.d.ts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { HtmlParser } from '@markuplint/html-parser';
|
|
2
|
+
/**
|
|
3
|
+
* Parser for PHP templates that extends the standard HTML parser.
|
|
4
|
+
*
|
|
5
|
+
* Configures the HTML parser to recognize PHP tag variants as opaque blocks:
|
|
6
|
+
* - `<?php ... ?>` (standard PHP code blocks; also matches unclosed tags at EOF)
|
|
7
|
+
* - `<?= ... ?>` (short echo / output tags)
|
|
8
|
+
* - `<? ... ?>` (short open tags; also matches unclosed tags at EOF)
|
|
9
|
+
*/
|
|
2
10
|
declare class PHPParser extends HtmlParser {
|
|
3
11
|
constructor();
|
|
4
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Singleton PHP parser instance for use by the markuplint engine.
|
|
15
|
+
*/
|
|
5
16
|
export declare const parser: PHPParser;
|
|
6
17
|
export {};
|
package/lib/parser.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { HtmlParser } from '@markuplint/html-parser';
|
|
2
|
+
/**
|
|
3
|
+
* Parser for PHP templates that extends the standard HTML parser.
|
|
4
|
+
*
|
|
5
|
+
* Configures the HTML parser to recognize PHP tag variants as opaque blocks:
|
|
6
|
+
* - `<?php ... ?>` (standard PHP code blocks; also matches unclosed tags at EOF)
|
|
7
|
+
* - `<?= ... ?>` (short echo / output tags)
|
|
8
|
+
* - `<? ... ?>` (short open tags; also matches unclosed tags at EOF)
|
|
9
|
+
*/
|
|
2
10
|
class PHPParser extends HtmlParser {
|
|
3
11
|
constructor() {
|
|
4
12
|
super({
|
|
@@ -22,4 +30,7 @@ class PHPParser extends HtmlParser {
|
|
|
22
30
|
});
|
|
23
31
|
}
|
|
24
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Singleton PHP parser instance for use by the markuplint engine.
|
|
35
|
+
*/
|
|
25
36
|
export const parser = new PHPParser();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markuplint/php-parser",
|
|
3
|
-
"version": "4.6.
|
|
3
|
+
"version": "4.6.23",
|
|
4
4
|
"description": "PHP parser for markuplint",
|
|
5
5
|
"repository": "git@github.com:markuplint/markuplint.git",
|
|
6
6
|
"author": "Yusuke Hirao <yusukehirao@me.com>",
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
"clean": "tsc --build --clean tsconfig.build.json"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@markuplint/html-parser": "4.6.
|
|
24
|
+
"@markuplint/html-parser": "4.6.23"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@markuplint/parser-utils": "4.8.
|
|
27
|
+
"@markuplint/parser-utils": "4.8.11"
|
|
28
28
|
},
|
|
29
|
-
"gitHead": "
|
|
29
|
+
"gitHead": "193ee7c1262bbed95424e38efdf1a8e56ff049f4"
|
|
30
30
|
}
|