php-parser-py 1.1.1__tar.gz → 1.2.0__tar.gz
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.
- php_parser_py-1.2.0/PKG-INFO +61 -0
- php_parser_py-1.2.0/README.md +50 -0
- {php_parser_py-1.1.1 → php_parser_py-1.2.0}/pyproject.toml +2 -2
- {php_parser_py-1.1.1 → php_parser_py-1.2.0}/src/php_parser_py/_ast.py +7 -17
- {php_parser_py-1.1.1 → php_parser_py-1.2.0}/src/php_parser_py/_parser.py +4 -11
- {php_parser_py-1.1.1 → php_parser_py-1.2.0}/src/php_parser_py/_printer.py +7 -9
- {php_parser_py-1.1.1 → php_parser_py-1.2.0}/src/php_parser_py/_runner.py +8 -24
- php_parser_py-1.1.1/PKG-INFO +0 -285
- php_parser_py-1.1.1/README.md +0 -274
- {php_parser_py-1.1.1 → php_parser_py-1.2.0}/src/php_parser_py/__init__.py +0 -0
- {php_parser_py-1.1.1 → php_parser_py-1.2.0}/src/php_parser_py/_edge.py +0 -0
- {php_parser_py-1.1.1 → php_parser_py-1.2.0}/src/php_parser_py/_exceptions.py +0 -0
- {php_parser_py-1.1.1 → php_parser_py-1.2.0}/src/php_parser_py/_node.py +0 -0
- {php_parser_py-1.1.1 → php_parser_py-1.2.0}/src/php_parser_py/_resources.py +0 -0
- {php_parser_py-1.1.1 → php_parser_py-1.2.0}/src/php_parser_py/resources/php-parser-4.19.4.zip +0 -0
- {php_parser_py-1.1.1 → php_parser_py-1.2.0}/src/php_parser_py/vendor/.extracted +0 -0
- {php_parser_py-1.1.1 → php_parser_py-1.2.0}/src/php_parser_py/vendor/LICENSE.txt +0 -0
- {php_parser_py-1.1.1 → php_parser_py-1.2.0}/src/php_parser_py/vendor/php-parser.phar +0 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: php-parser-py
|
|
3
|
+
Version: 1.2.0
|
|
4
|
+
Summary: Python wrapper for PHP-Parser using cpg2py
|
|
5
|
+
Author: samhsu-dev
|
|
6
|
+
Author-email: samhsu-dev <yxu166@jh.edu>
|
|
7
|
+
Requires-Dist: cpg2py>=1.2.0
|
|
8
|
+
Requires-Dist: static-php-py
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# php-parser-py
|
|
13
|
+
|
|
14
|
+
Python wrapper for [nikic/PHP-Parser](https://github.com/nikic/PHP-Parser) built on the [cpg2py](https://github.com/jhu-seclab/cpg2py) graph framework.
|
|
15
|
+
|
|
16
|
+
- **Automated**: Uses [static-php-cli](https://github.com/static-php-cli/static-php-cli) binaries (no local PHP required).
|
|
17
|
+
- **Graph-Based**: AST nodes are stored in a queryable graph database.
|
|
18
|
+
- **Accurate**: Full support for PHP-Parser 4.19.4 AST structure.
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
```bash
|
|
22
|
+
pip install php-parser-py
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from php_parser_py import parse_file, parse_project
|
|
27
|
+
|
|
28
|
+
# 1. Parse
|
|
29
|
+
ast = parse_file("src/User.php")
|
|
30
|
+
# ast = parse_project("src/")
|
|
31
|
+
|
|
32
|
+
# 2. Traverse
|
|
33
|
+
files = ast.file_nodes()
|
|
34
|
+
|
|
35
|
+
# Find specific nodes
|
|
36
|
+
functions = ast.nodes(lambda n: n.node_type == "Stmt_Function")
|
|
37
|
+
|
|
38
|
+
# Get properties (scalars)
|
|
39
|
+
for func in functions:
|
|
40
|
+
print(f"Function: {func['namespacedName']}")
|
|
41
|
+
|
|
42
|
+
# Get child nodes (structure)
|
|
43
|
+
for func in functions:
|
|
44
|
+
# Use helper method or traverse edges
|
|
45
|
+
params = [c for c in ast.succ(func) if c.node_type == "Param"]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Documentation
|
|
49
|
+
|
|
50
|
+
- **[Architecture & Design](docs/design.md)**
|
|
51
|
+
Detailed design documentation covering internal architecture, class specifications, and design rationale.
|
|
52
|
+
|
|
53
|
+
- **[AST Structure Reference](docs/libs/php_parser_ast.md)**
|
|
54
|
+
Complete table of all node types, subnodes (children), and properties. Verified against PHP-Parser source.
|
|
55
|
+
|
|
56
|
+
- **[Graph Traversal Guide](docs/libs/cpg2py_traversal.md)**
|
|
57
|
+
How to navigate the graph (`succ`, `prev`), access properties, and modify the graph (`set_property`).
|
|
58
|
+
|
|
59
|
+
## Requirements
|
|
60
|
+
- Python ≥ 3.11
|
|
61
|
+
- (Optional) Custom PHP binary can be configured via `Parser` constructor.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# php-parser-py
|
|
2
|
+
|
|
3
|
+
Python wrapper for [nikic/PHP-Parser](https://github.com/nikic/PHP-Parser) built on the [cpg2py](https://github.com/jhu-seclab/cpg2py) graph framework.
|
|
4
|
+
|
|
5
|
+
- **Automated**: Uses [static-php-cli](https://github.com/static-php-cli/static-php-cli) binaries (no local PHP required).
|
|
6
|
+
- **Graph-Based**: AST nodes are stored in a queryable graph database.
|
|
7
|
+
- **Accurate**: Full support for PHP-Parser 4.19.4 AST structure.
|
|
8
|
+
|
|
9
|
+
## Quick Start
|
|
10
|
+
```bash
|
|
11
|
+
pip install php-parser-py
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
from php_parser_py import parse_file, parse_project
|
|
16
|
+
|
|
17
|
+
# 1. Parse
|
|
18
|
+
ast = parse_file("src/User.php")
|
|
19
|
+
# ast = parse_project("src/")
|
|
20
|
+
|
|
21
|
+
# 2. Traverse
|
|
22
|
+
files = ast.file_nodes()
|
|
23
|
+
|
|
24
|
+
# Find specific nodes
|
|
25
|
+
functions = ast.nodes(lambda n: n.node_type == "Stmt_Function")
|
|
26
|
+
|
|
27
|
+
# Get properties (scalars)
|
|
28
|
+
for func in functions:
|
|
29
|
+
print(f"Function: {func['namespacedName']}")
|
|
30
|
+
|
|
31
|
+
# Get child nodes (structure)
|
|
32
|
+
for func in functions:
|
|
33
|
+
# Use helper method or traverse edges
|
|
34
|
+
params = [c for c in ast.succ(func) if c.node_type == "Param"]
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Documentation
|
|
38
|
+
|
|
39
|
+
- **[Architecture & Design](docs/design.md)**
|
|
40
|
+
Detailed design documentation covering internal architecture, class specifications, and design rationale.
|
|
41
|
+
|
|
42
|
+
- **[AST Structure Reference](docs/libs/php_parser_ast.md)**
|
|
43
|
+
Complete table of all node types, subnodes (children), and properties. Verified against PHP-Parser source.
|
|
44
|
+
|
|
45
|
+
- **[Graph Traversal Guide](docs/libs/cpg2py_traversal.md)**
|
|
46
|
+
How to navigate the graph (`succ`, `prev`), access properties, and modify the graph (`set_property`).
|
|
47
|
+
|
|
48
|
+
## Requirements
|
|
49
|
+
- Python ≥ 3.11
|
|
50
|
+
- (Optional) Custom PHP binary can be configured via `Parser` constructor.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "php-parser-py"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.2.0"
|
|
4
4
|
description = "Python wrapper for PHP-Parser using cpg2py"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -8,7 +8,7 @@ authors = [
|
|
|
8
8
|
]
|
|
9
9
|
requires-python = ">=3.11"
|
|
10
10
|
dependencies = [
|
|
11
|
-
"cpg2py>=1.
|
|
11
|
+
"cpg2py>=1.2.0",
|
|
12
12
|
"static-php-py",
|
|
13
13
|
]
|
|
14
14
|
|
|
@@ -77,35 +77,25 @@ class AST(AbcGraphQuerier[Node, Edge]):
|
|
|
77
77
|
return None
|
|
78
78
|
return Edge(self._storage, fid, tid, eid)
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
"""Return the root project node of the AST.
|
|
80
|
+
def project_node(self) -> Optional[Node]:
|
|
81
|
+
"""Return the project node.
|
|
83
82
|
|
|
84
|
-
The
|
|
83
|
+
The project node is the root of the AST with ID "project".
|
|
85
84
|
|
|
86
85
|
Returns:
|
|
87
|
-
|
|
86
|
+
Project Node instance or None if not found.
|
|
88
87
|
"""
|
|
89
88
|
if not self._storage.contains_node(self._root_node_id):
|
|
90
89
|
return None
|
|
91
90
|
return Node(self._storage, self._root_node_id)
|
|
92
91
|
|
|
93
|
-
|
|
94
|
-
def project_node(self) -> Optional[Node]:
|
|
95
|
-
"""Return the project node (alias for root_node).
|
|
96
|
-
|
|
97
|
-
Returns:
|
|
98
|
-
Project Node instance or None if not found.
|
|
99
|
-
"""
|
|
100
|
-
return self.root_node
|
|
101
|
-
|
|
102
|
-
def files(self) -> list[Node]:
|
|
92
|
+
def file_nodes(self) -> list[Node]:
|
|
103
93
|
"""Return all file nodes in the project.
|
|
104
94
|
|
|
105
95
|
Returns:
|
|
106
96
|
List of File Node instances.
|
|
107
97
|
"""
|
|
108
|
-
if not self.
|
|
98
|
+
if not self.project_node():
|
|
109
99
|
return []
|
|
110
100
|
|
|
111
101
|
file_nodes = []
|
|
@@ -204,7 +194,7 @@ class AST(AbcGraphQuerier[Node, Edge]):
|
|
|
204
194
|
top_level_nodes = self._get_file_statements(file_hash)
|
|
205
195
|
else:
|
|
206
196
|
# Try to export from file structure first
|
|
207
|
-
file_nodes = self.
|
|
197
|
+
file_nodes = self.file_nodes()
|
|
208
198
|
if file_nodes:
|
|
209
199
|
# Export all files (flattened)
|
|
210
200
|
top_level_nodes = []
|
|
@@ -6,6 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
from typing import Any, Callable, Optional
|
|
7
7
|
|
|
8
8
|
from cpg2py import Storage
|
|
9
|
+
from static_php_py import PHP
|
|
9
10
|
|
|
10
11
|
from php_parser_py._ast import AST
|
|
11
12
|
from php_parser_py._exceptions import ParseError, RunnerError
|
|
@@ -27,21 +28,13 @@ class Parser:
|
|
|
27
28
|
_runner: Runner instance for PHP-Parser invocation.
|
|
28
29
|
"""
|
|
29
30
|
|
|
30
|
-
def __init__(
|
|
31
|
-
self,
|
|
32
|
-
php_binary_path: Optional[Path] = None,
|
|
33
|
-
php_binary_url: Optional[str] = None,
|
|
34
|
-
) -> None:
|
|
31
|
+
def __init__(self, php: Optional[PHP] = None) -> None:
|
|
35
32
|
"""Initialize Parser with Runner.
|
|
36
33
|
|
|
37
34
|
Args:
|
|
38
|
-
|
|
39
|
-
php_binary_url: Optional URL to download PHP binary from.
|
|
35
|
+
php: Optional PHP instance. If not provided, uses builtin PHP.
|
|
40
36
|
"""
|
|
41
|
-
self._runner = Runner(
|
|
42
|
-
php_binary_path=php_binary_path,
|
|
43
|
-
php_binary_url=php_binary_url,
|
|
44
|
-
)
|
|
37
|
+
self._runner = Runner(php=php)
|
|
45
38
|
|
|
46
39
|
def parse_code(self, code: str) -> list[Node]:
|
|
47
40
|
"""Parse PHP code string into a list of top-level statement nodes.
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"""PrettyPrinter class for PHP code generation."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from pathlib import Path
|
|
5
4
|
from typing import Optional
|
|
6
5
|
|
|
6
|
+
from static_php_py import PHP
|
|
7
|
+
|
|
7
8
|
from php_parser_py._ast import AST
|
|
8
9
|
from php_parser_py._runner import Runner
|
|
9
10
|
|
|
@@ -22,18 +23,15 @@ class PrettyPrinter:
|
|
|
22
23
|
|
|
23
24
|
def __init__(
|
|
24
25
|
self,
|
|
25
|
-
|
|
26
|
-
php_binary_url: Optional[str] = None,
|
|
26
|
+
php: Optional[PHP] = None,
|
|
27
27
|
) -> None:
|
|
28
28
|
"""Initialize PrettyPrinter with Runner.
|
|
29
29
|
|
|
30
30
|
Args:
|
|
31
|
-
|
|
32
|
-
php_binary_url: Optional URL to download PHP binary from.
|
|
31
|
+
php: Optional PHP instance. If not provided, uses builtin PHP.
|
|
33
32
|
"""
|
|
34
33
|
self._runner = Runner(
|
|
35
|
-
|
|
36
|
-
php_binary_url=php_binary_url,
|
|
34
|
+
php=php,
|
|
37
35
|
)
|
|
38
36
|
|
|
39
37
|
def print(self, ast: AST) -> dict[str, str]:
|
|
@@ -50,7 +48,7 @@ class PrettyPrinter:
|
|
|
50
48
|
Raises:
|
|
51
49
|
RunnerError: If PHP-Parser execution fails.
|
|
52
50
|
"""
|
|
53
|
-
file_nodes = ast.
|
|
51
|
+
file_nodes = ast.file_nodes()
|
|
54
52
|
|
|
55
53
|
if not file_nodes:
|
|
56
54
|
# No file structure - export all statements as a single code block
|
|
@@ -98,7 +96,7 @@ class PrettyPrinter:
|
|
|
98
96
|
RunnerError: If PHP-Parser execution fails.
|
|
99
97
|
"""
|
|
100
98
|
# Find file node by its relative path property
|
|
101
|
-
for file_node in ast.
|
|
99
|
+
for file_node in ast.file_nodes():
|
|
102
100
|
if file_node.get_property("path") == relative_path:
|
|
103
101
|
file_hash = file_node.id
|
|
104
102
|
json_str = ast.to_json(file_hash=file_hash)
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import json
|
|
4
4
|
import logging
|
|
5
5
|
import subprocess
|
|
6
|
-
from pathlib import Path
|
|
7
6
|
from typing import Any, Optional
|
|
8
7
|
|
|
9
8
|
from static_php_py import PHP
|
|
@@ -26,41 +25,26 @@ class Runner:
|
|
|
26
25
|
_vendor_dir: Path to directory containing PHP-Parser PHAR.
|
|
27
26
|
"""
|
|
28
27
|
|
|
29
|
-
def __init__(
|
|
30
|
-
|
|
31
|
-
php_binary_path: Optional[Path] = None,
|
|
32
|
-
php_binary_url: Optional[str] = None,
|
|
33
|
-
) -> None:
|
|
34
|
-
"""Initialize Runner with PHP binary and PHP-Parser paths.
|
|
28
|
+
def __init__(self, php: Optional[PHP] = None) -> None:
|
|
29
|
+
"""Initialize Runner with PHP binary wrapper.
|
|
35
30
|
|
|
36
31
|
Args:
|
|
37
|
-
|
|
38
|
-
php_binary_url: Optional URL to download PHP binary from.
|
|
32
|
+
php: Optional PHP instance. If not provided, uses builtin PHP.
|
|
39
33
|
|
|
40
34
|
Raises:
|
|
41
|
-
RunnerError: If PHP binary
|
|
35
|
+
RunnerError: If PHP binary cannot be located.
|
|
42
36
|
"""
|
|
43
37
|
self._vendor_dir = ensure_php_parser_extracted()
|
|
44
38
|
|
|
45
|
-
# Determine PHP binary source with priority: custom path > custom URL > built-in
|
|
46
39
|
try:
|
|
47
|
-
if
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
elif php_binary_url is not None:
|
|
51
|
-
# Download PHP binary from custom URL
|
|
52
|
-
php = PHP.remote(php_binary_url)
|
|
53
|
-
else:
|
|
54
|
-
# Use built-in PHP binary from static-php-py
|
|
55
|
-
php = PHP.builtin()
|
|
56
|
-
|
|
57
|
-
self._php_binary = php.path()
|
|
58
|
-
|
|
40
|
+
self._php = php if php is not None else PHP.builtin()
|
|
41
|
+
self._php_binary = self._php.path()
|
|
42
|
+
|
|
59
43
|
except BinaryNotFoundError as e:
|
|
60
44
|
raise RunnerError(
|
|
61
45
|
f"PHP binary not found: {e}. "
|
|
62
46
|
"The built-in PHP binary may not be available for your platform. "
|
|
63
|
-
"Please provide a custom PHP
|
|
47
|
+
"Please provide a custom PHP instance.",
|
|
64
48
|
exit_code=1,
|
|
65
49
|
) from e
|
|
66
50
|
except DownloadError as e:
|
php_parser_py-1.1.1/PKG-INFO
DELETED
|
@@ -1,285 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.3
|
|
2
|
-
Name: php-parser-py
|
|
3
|
-
Version: 1.1.1
|
|
4
|
-
Summary: Python wrapper for PHP-Parser using cpg2py
|
|
5
|
-
Author: samhsu-dev
|
|
6
|
-
Author-email: samhsu-dev <yxu166@jh.edu>
|
|
7
|
-
Requires-Dist: cpg2py>=1.1.0
|
|
8
|
-
Requires-Dist: static-php-py
|
|
9
|
-
Requires-Python: >=3.11
|
|
10
|
-
Description-Content-Type: text/markdown
|
|
11
|
-
|
|
12
|
-
# php-parser-py
|
|
13
|
-
|
|
14
|
-
Python wrapper for PHP-Parser using cpg2py's graph framework.
|
|
15
|
-
|
|
16
|
-
## Overview
|
|
17
|
-
|
|
18
|
-
`php-parser-py` provides a Python interface to [PHP-Parser](https://github.com/nikic/PHP-Parser), enabling Abstract Syntax Tree (AST) parsing and manipulation of PHP code. The library integrates with [cpg2py](https://github.com/jhu-seclab/cpg2py)'s graph framework for powerful graph-based code analysis.
|
|
19
|
-
|
|
20
|
-
## Features
|
|
21
|
-
|
|
22
|
-
- **Native PHP-Parser Integration**: Delegates all parsing and code generation to PHP-Parser for maximum compatibility
|
|
23
|
-
- **Zero-Configuration PHP**: Bundled PHP binaries via static-php-py - no system PHP installation required
|
|
24
|
-
- **Graph-Based Analysis**: Uses cpg2py's graph framework for AST traversal and querying
|
|
25
|
-
- **Dynamic Node Types**: No hardcoded PHP node types - all types come from PHP-Parser's JSON output
|
|
26
|
-
- **Lossless Round-Trip**: Parse → Modify → Generate code with full fidelity
|
|
27
|
-
- **Cross-Platform Support**: Pre-built PHP 8.4 binaries for Linux (x86_64/aarch64), macOS (x86_64/arm64), and Windows (x64)
|
|
28
|
-
|
|
29
|
-
## Installation
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
pip install php-parser-py
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
**Note**: Platform-specific wheels include pre-built PHP 8.4 binaries. No separate PHP installation required!
|
|
36
|
-
|
|
37
|
-
## Quick Start
|
|
38
|
-
|
|
39
|
-
### Parse PHP Code (Without Project Structure)
|
|
40
|
-
|
|
41
|
-
```python
|
|
42
|
-
from php_parser_py import parse_code
|
|
43
|
-
|
|
44
|
-
# Parse PHP code into raw statement nodes
|
|
45
|
-
code = """<?php
|
|
46
|
-
function greet($name) {
|
|
47
|
-
echo "Hello, $name!";
|
|
48
|
-
}
|
|
49
|
-
"""
|
|
50
|
-
|
|
51
|
-
nodes = parse_code(code)
|
|
52
|
-
# Returns list of Node instances (no project/file structure)
|
|
53
|
-
|
|
54
|
-
for node in nodes:
|
|
55
|
-
print(f"Node type: {node.node_type}")
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### Parse PHP File (With Project Structure)
|
|
59
|
-
|
|
60
|
-
```python
|
|
61
|
-
from php_parser_py import parse_file
|
|
62
|
-
|
|
63
|
-
# Parse a single file with project/file structure
|
|
64
|
-
ast = parse_file("example.php")
|
|
65
|
-
|
|
66
|
-
# Access project and file nodes
|
|
67
|
-
project = ast.project_node
|
|
68
|
-
files = ast.files()
|
|
69
|
-
print(f"Project has {len(files)} file(s)")
|
|
70
|
-
|
|
71
|
-
# Get file containing a specific node
|
|
72
|
-
stmt_node = ast.first_node(lambda n: n.node_type == "Stmt_Function")
|
|
73
|
-
file_node = ast.get_file(stmt_node.id)
|
|
74
|
-
print(f"Function is in file: {file_node.get_property('filePath')}")
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### Parse Project Directory
|
|
78
|
-
|
|
79
|
-
```python
|
|
80
|
-
from php_parser_py import parse_project
|
|
81
|
-
from pathlib import Path
|
|
82
|
-
|
|
83
|
-
# Parse all PHP files in a project directory (recursively)
|
|
84
|
-
ast = parse_project("/path/to/project")
|
|
85
|
-
|
|
86
|
-
# Access all files
|
|
87
|
-
for file_node in ast.files():
|
|
88
|
-
print(f"File: {file_node.get_property('filePath')}")
|
|
89
|
-
# Get statements in this file
|
|
90
|
-
for stmt in ast.succ(file_node):
|
|
91
|
-
print(f" Statement: {stmt.node_type}")
|
|
92
|
-
|
|
93
|
-
# Access project and file paths
|
|
94
|
-
project = ast.project_node
|
|
95
|
-
print(f"Project root: {project.get_property('path')}")
|
|
96
|
-
|
|
97
|
-
for file_node in ast.files():
|
|
98
|
-
print(f"File relative path: {file_node.get_property('path')}")
|
|
99
|
-
print(f"File absolute path: {file_node.get_property('filePath')}")
|
|
100
|
-
|
|
101
|
-
# Use custom file filter (e.g., include .php and .phtml files)
|
|
102
|
-
ast = parse_project(
|
|
103
|
-
"/path/to/project",
|
|
104
|
-
file_filter=lambda p: p.suffix in ['.php', '.phtml']
|
|
105
|
-
)
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
### Traverse AST
|
|
109
|
-
|
|
110
|
-
```python
|
|
111
|
-
from php_parser_py import parse_file
|
|
112
|
-
|
|
113
|
-
ast = parse_file("example.php")
|
|
114
|
-
|
|
115
|
-
# Find all echo statements
|
|
116
|
-
for echo in ast.nodes(lambda n: n.node_type == "Stmt_Echo"):
|
|
117
|
-
print(f"Echo at line {echo.start_line}")
|
|
118
|
-
|
|
119
|
-
# Get children of a node (via PARENT_OF edges)
|
|
120
|
-
for child in ast.succ(some_node):
|
|
121
|
-
print(f"Child type: {child.node_type}")
|
|
122
|
-
|
|
123
|
-
# Get all descendants
|
|
124
|
-
for descendant in ast.descendants(some_node):
|
|
125
|
-
print(f"Descendant: {descendant.node_type}")
|
|
126
|
-
|
|
127
|
-
# Get file containing a node
|
|
128
|
-
stmt_node = ast.first_node(lambda n: n.node_type == "Stmt_Function")
|
|
129
|
-
file_node = ast.get_file(stmt_node.id)
|
|
130
|
-
if file_node:
|
|
131
|
-
print(f"Function is in: {file_node.get_property('filePath')}")
|
|
132
|
-
|
|
133
|
-
# Access project structure
|
|
134
|
-
project = ast.project_node
|
|
135
|
-
files = ast.files()
|
|
136
|
-
for file_node in files:
|
|
137
|
-
print(f"File: {file_node.get_property('filePath')}")
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
### Generate PHP Code
|
|
141
|
-
|
|
142
|
-
```python
|
|
143
|
-
from php_parser_py import parse_file, parse_project, PrettyPrinter
|
|
144
|
-
|
|
145
|
-
# Parse single file
|
|
146
|
-
ast = parse_file("example.php")
|
|
147
|
-
|
|
148
|
-
# Generate code (returns dict mapping file paths to code)
|
|
149
|
-
printer = PrettyPrinter()
|
|
150
|
-
generated = printer.print(ast)
|
|
151
|
-
for file_path, code in generated.items():
|
|
152
|
-
print(f"File: {file_path}")
|
|
153
|
-
print(code)
|
|
154
|
-
|
|
155
|
-
# Parse project directory (recursively finds all PHP files)
|
|
156
|
-
ast = parse_project("/path/to/project")
|
|
157
|
-
|
|
158
|
-
# Generate code for all files
|
|
159
|
-
generated = printer.print(ast)
|
|
160
|
-
# Returns: {"src/file1.php": "...", "src/file2.php": "..."}
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
## Advanced Usage
|
|
164
|
-
|
|
165
|
-
### Custom PHP Binary
|
|
166
|
-
|
|
167
|
-
If you need to use a specific PHP version or custom binary:
|
|
168
|
-
|
|
169
|
-
```python
|
|
170
|
-
from php_parser_py import Parser
|
|
171
|
-
from pathlib import Path
|
|
172
|
-
|
|
173
|
-
# Use local PHP binary
|
|
174
|
-
parser = Parser(php_binary_path=Path("/usr/local/bin/php"))
|
|
175
|
-
ast = parser.parse("<?php echo 'hello';")
|
|
176
|
-
|
|
177
|
-
# Or download from remote URL
|
|
178
|
-
parser = Parser(php_binary_url="https://example.com/php-8.4.zip")
|
|
179
|
-
ast = parser.parse("<?php echo 'hello';")
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
## API Reference
|
|
183
|
-
|
|
184
|
-
### Main Functions
|
|
185
|
-
|
|
186
|
-
- **`parse_code(code: str) -> list[Node]`**: Parse PHP code string into raw statement nodes (no project/file structure)
|
|
187
|
-
- **`parse(code: str) -> AST`**: Parse PHP code string and return AST with project/file structure (backward compatibility)
|
|
188
|
-
- **`parse_file(path: str) -> AST`**: Parse a single PHP file and return AST with project/file structure
|
|
189
|
-
- **`parse_project(project_path: str, file_filter: Callable[[Path], bool] = lambda p: p.suffix == '.php') -> AST`**: Parse all PHP files in a project directory into a single AST with project structure. `file_filter` function to customize which files are included (defaults to `.php` suffix check)
|
|
190
|
-
|
|
191
|
-
### Classes
|
|
192
|
-
|
|
193
|
-
- **`AST`**: Represents the complete AST as a graph
|
|
194
|
-
- `node(id)`: Get node by ID
|
|
195
|
-
- `nodes(predicate)`: Iterate nodes matching predicate
|
|
196
|
-
- `first_node(predicate)`: Get first matching node
|
|
197
|
-
- `succ(node, predicate)`: Get successor nodes (children via PARENT_OF edges)
|
|
198
|
-
- `prev(node, predicate)`: Get predecessor nodes (parents via PARENT_OF edges)
|
|
199
|
-
- `descendants(node, predicate)`: Get all descendants via BFS
|
|
200
|
-
- `ancestors(node, predicate)`: Get all ancestors via BFS
|
|
201
|
-
- `root_node`: Property returning the root/project node
|
|
202
|
-
- `project_node`: Property returning the project node (alias for root_node)
|
|
203
|
-
- `files()`: Get all file nodes in the project
|
|
204
|
-
- `get_file(node_id)`: Get the file node containing the given node
|
|
205
|
-
- `to_json()`: Reconstruct PHP-Parser JSON
|
|
206
|
-
|
|
207
|
-
- **`Node`**: Wraps a single AST node
|
|
208
|
-
- `id`: Node identifier
|
|
209
|
-
- `node_type`: Node type (e.g., "Stmt_Function") - returns `nodeType` property
|
|
210
|
-
- `all_properties`: All node properties as dict
|
|
211
|
-
- `get_property(*names)`: Get property value
|
|
212
|
-
- `start_line`, `end_line`: Line number properties
|
|
213
|
-
- `__getitem__(key)`: Dict-like access (e.g., `node["name"]`)
|
|
214
|
-
- `get(key, default)`: Get property with default value
|
|
215
|
-
|
|
216
|
-
- **`Parser`**: Parses PHP code
|
|
217
|
-
- `__init__(php_binary_path=None, php_binary_url=None)`: Initialize parser
|
|
218
|
-
- `php_binary_path`: Optional path to local PHP binary
|
|
219
|
-
- `php_binary_url`: Optional URL to download PHP binary from
|
|
220
|
-
- `parse_code(code)`: Parse code string into list of nodes
|
|
221
|
-
- `parse(code)`: Parse code string into AST (backward compatibility)
|
|
222
|
-
- `parse_file(path)`: Parse file into AST with project/file structure
|
|
223
|
-
- `parse_project(project_path)`: Parse project directory into AST with project structure
|
|
224
|
-
|
|
225
|
-
- **`PrettyPrinter`**: Generates PHP code
|
|
226
|
-
- `__init__(php_binary_path=None, php_binary_url=None)`: Initialize printer
|
|
227
|
-
- `php_binary_path`: Optional path to local PHP binary
|
|
228
|
-
- `php_binary_url`: Optional URL to download PHP binary from
|
|
229
|
-
- `print(ast) -> dict[str, str]`: Generate code from AST, returns dictionary mapping file paths to generated code
|
|
230
|
-
- Returns: `{file_path: code_string, ...}` for each file in the AST
|
|
231
|
-
- If AST has no file structure, returns `{"": code_string}`
|
|
232
|
-
|
|
233
|
-
### Exceptions
|
|
234
|
-
|
|
235
|
-
- **`ParseError`**: Raised when PHP-Parser reports syntax error
|
|
236
|
-
- **`RunnerError`**: Raised when PHP execution fails
|
|
237
|
-
|
|
238
|
-
## AST Structure
|
|
239
|
-
|
|
240
|
-
The library supports three parsing modes:
|
|
241
|
-
|
|
242
|
-
1. **Code Parsing** (`parse_code`): Returns flat list of statement nodes
|
|
243
|
-
- Node IDs: `node_1`, `node_2`, ...
|
|
244
|
-
- No project/file structure
|
|
245
|
-
|
|
246
|
-
2. **File Parsing** (`parse_file`): Creates project → file → statements hierarchy
|
|
247
|
-
- Project node: `"project"` (fixed ID)
|
|
248
|
-
- File node: 8-character hex hash (e.g., `"a1b2c3d4"`)
|
|
249
|
-
- Statement nodes: `{hex}_1`, `{hex}_2`, ...
|
|
250
|
-
|
|
251
|
-
3. **Project Parsing** (`parse_project`): Creates project → multiple files → statements hierarchy
|
|
252
|
-
- Single project node: `"project"` (fixed ID)
|
|
253
|
-
- Multiple file nodes: Each with unique hex hash
|
|
254
|
-
- Statement nodes: Prefixed with their file's hash
|
|
255
|
-
|
|
256
|
-
## Architecture
|
|
257
|
-
|
|
258
|
-
The library follows a delegation-based architecture:
|
|
259
|
-
|
|
260
|
-
1. **PHP-Parser** handles all parsing and code generation
|
|
261
|
-
2. **cpg2py Storage** stores the AST as a graph
|
|
262
|
-
3. **Python classes** provide convenient access to the graph
|
|
263
|
-
|
|
264
|
-
This design ensures:
|
|
265
|
-
- No need to maintain PHP node type definitions in Python
|
|
266
|
-
- Automatic compatibility with PHP-Parser updates
|
|
267
|
-
- Full access to PHP-Parser's features
|
|
268
|
-
- Support for multi-file project analysis with merge capabilities
|
|
269
|
-
|
|
270
|
-
## Requirements
|
|
271
|
-
|
|
272
|
-
- Python >= 3.11
|
|
273
|
-
- cpg2py
|
|
274
|
-
- static-php-py (bundled PHP binaries)
|
|
275
|
-
|
|
276
|
-
## License
|
|
277
|
-
|
|
278
|
-
MIT License - see LICENSE file for details.
|
|
279
|
-
|
|
280
|
-
## Documentation
|
|
281
|
-
|
|
282
|
-
For detailed design documentation, see:
|
|
283
|
-
- [Design Document](docs/design.md)
|
|
284
|
-
- [Idea Document](docs/idea.md)
|
|
285
|
-
- [API Demo](docs/api_demo.ipynb)
|
php_parser_py-1.1.1/README.md
DELETED
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
# php-parser-py
|
|
2
|
-
|
|
3
|
-
Python wrapper for PHP-Parser using cpg2py's graph framework.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
`php-parser-py` provides a Python interface to [PHP-Parser](https://github.com/nikic/PHP-Parser), enabling Abstract Syntax Tree (AST) parsing and manipulation of PHP code. The library integrates with [cpg2py](https://github.com/jhu-seclab/cpg2py)'s graph framework for powerful graph-based code analysis.
|
|
8
|
-
|
|
9
|
-
## Features
|
|
10
|
-
|
|
11
|
-
- **Native PHP-Parser Integration**: Delegates all parsing and code generation to PHP-Parser for maximum compatibility
|
|
12
|
-
- **Zero-Configuration PHP**: Bundled PHP binaries via static-php-py - no system PHP installation required
|
|
13
|
-
- **Graph-Based Analysis**: Uses cpg2py's graph framework for AST traversal and querying
|
|
14
|
-
- **Dynamic Node Types**: No hardcoded PHP node types - all types come from PHP-Parser's JSON output
|
|
15
|
-
- **Lossless Round-Trip**: Parse → Modify → Generate code with full fidelity
|
|
16
|
-
- **Cross-Platform Support**: Pre-built PHP 8.4 binaries for Linux (x86_64/aarch64), macOS (x86_64/arm64), and Windows (x64)
|
|
17
|
-
|
|
18
|
-
## Installation
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
pip install php-parser-py
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
**Note**: Platform-specific wheels include pre-built PHP 8.4 binaries. No separate PHP installation required!
|
|
25
|
-
|
|
26
|
-
## Quick Start
|
|
27
|
-
|
|
28
|
-
### Parse PHP Code (Without Project Structure)
|
|
29
|
-
|
|
30
|
-
```python
|
|
31
|
-
from php_parser_py import parse_code
|
|
32
|
-
|
|
33
|
-
# Parse PHP code into raw statement nodes
|
|
34
|
-
code = """<?php
|
|
35
|
-
function greet($name) {
|
|
36
|
-
echo "Hello, $name!";
|
|
37
|
-
}
|
|
38
|
-
"""
|
|
39
|
-
|
|
40
|
-
nodes = parse_code(code)
|
|
41
|
-
# Returns list of Node instances (no project/file structure)
|
|
42
|
-
|
|
43
|
-
for node in nodes:
|
|
44
|
-
print(f"Node type: {node.node_type}")
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### Parse PHP File (With Project Structure)
|
|
48
|
-
|
|
49
|
-
```python
|
|
50
|
-
from php_parser_py import parse_file
|
|
51
|
-
|
|
52
|
-
# Parse a single file with project/file structure
|
|
53
|
-
ast = parse_file("example.php")
|
|
54
|
-
|
|
55
|
-
# Access project and file nodes
|
|
56
|
-
project = ast.project_node
|
|
57
|
-
files = ast.files()
|
|
58
|
-
print(f"Project has {len(files)} file(s)")
|
|
59
|
-
|
|
60
|
-
# Get file containing a specific node
|
|
61
|
-
stmt_node = ast.first_node(lambda n: n.node_type == "Stmt_Function")
|
|
62
|
-
file_node = ast.get_file(stmt_node.id)
|
|
63
|
-
print(f"Function is in file: {file_node.get_property('filePath')}")
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### Parse Project Directory
|
|
67
|
-
|
|
68
|
-
```python
|
|
69
|
-
from php_parser_py import parse_project
|
|
70
|
-
from pathlib import Path
|
|
71
|
-
|
|
72
|
-
# Parse all PHP files in a project directory (recursively)
|
|
73
|
-
ast = parse_project("/path/to/project")
|
|
74
|
-
|
|
75
|
-
# Access all files
|
|
76
|
-
for file_node in ast.files():
|
|
77
|
-
print(f"File: {file_node.get_property('filePath')}")
|
|
78
|
-
# Get statements in this file
|
|
79
|
-
for stmt in ast.succ(file_node):
|
|
80
|
-
print(f" Statement: {stmt.node_type}")
|
|
81
|
-
|
|
82
|
-
# Access project and file paths
|
|
83
|
-
project = ast.project_node
|
|
84
|
-
print(f"Project root: {project.get_property('path')}")
|
|
85
|
-
|
|
86
|
-
for file_node in ast.files():
|
|
87
|
-
print(f"File relative path: {file_node.get_property('path')}")
|
|
88
|
-
print(f"File absolute path: {file_node.get_property('filePath')}")
|
|
89
|
-
|
|
90
|
-
# Use custom file filter (e.g., include .php and .phtml files)
|
|
91
|
-
ast = parse_project(
|
|
92
|
-
"/path/to/project",
|
|
93
|
-
file_filter=lambda p: p.suffix in ['.php', '.phtml']
|
|
94
|
-
)
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### Traverse AST
|
|
98
|
-
|
|
99
|
-
```python
|
|
100
|
-
from php_parser_py import parse_file
|
|
101
|
-
|
|
102
|
-
ast = parse_file("example.php")
|
|
103
|
-
|
|
104
|
-
# Find all echo statements
|
|
105
|
-
for echo in ast.nodes(lambda n: n.node_type == "Stmt_Echo"):
|
|
106
|
-
print(f"Echo at line {echo.start_line}")
|
|
107
|
-
|
|
108
|
-
# Get children of a node (via PARENT_OF edges)
|
|
109
|
-
for child in ast.succ(some_node):
|
|
110
|
-
print(f"Child type: {child.node_type}")
|
|
111
|
-
|
|
112
|
-
# Get all descendants
|
|
113
|
-
for descendant in ast.descendants(some_node):
|
|
114
|
-
print(f"Descendant: {descendant.node_type}")
|
|
115
|
-
|
|
116
|
-
# Get file containing a node
|
|
117
|
-
stmt_node = ast.first_node(lambda n: n.node_type == "Stmt_Function")
|
|
118
|
-
file_node = ast.get_file(stmt_node.id)
|
|
119
|
-
if file_node:
|
|
120
|
-
print(f"Function is in: {file_node.get_property('filePath')}")
|
|
121
|
-
|
|
122
|
-
# Access project structure
|
|
123
|
-
project = ast.project_node
|
|
124
|
-
files = ast.files()
|
|
125
|
-
for file_node in files:
|
|
126
|
-
print(f"File: {file_node.get_property('filePath')}")
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### Generate PHP Code
|
|
130
|
-
|
|
131
|
-
```python
|
|
132
|
-
from php_parser_py import parse_file, parse_project, PrettyPrinter
|
|
133
|
-
|
|
134
|
-
# Parse single file
|
|
135
|
-
ast = parse_file("example.php")
|
|
136
|
-
|
|
137
|
-
# Generate code (returns dict mapping file paths to code)
|
|
138
|
-
printer = PrettyPrinter()
|
|
139
|
-
generated = printer.print(ast)
|
|
140
|
-
for file_path, code in generated.items():
|
|
141
|
-
print(f"File: {file_path}")
|
|
142
|
-
print(code)
|
|
143
|
-
|
|
144
|
-
# Parse project directory (recursively finds all PHP files)
|
|
145
|
-
ast = parse_project("/path/to/project")
|
|
146
|
-
|
|
147
|
-
# Generate code for all files
|
|
148
|
-
generated = printer.print(ast)
|
|
149
|
-
# Returns: {"src/file1.php": "...", "src/file2.php": "..."}
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
## Advanced Usage
|
|
153
|
-
|
|
154
|
-
### Custom PHP Binary
|
|
155
|
-
|
|
156
|
-
If you need to use a specific PHP version or custom binary:
|
|
157
|
-
|
|
158
|
-
```python
|
|
159
|
-
from php_parser_py import Parser
|
|
160
|
-
from pathlib import Path
|
|
161
|
-
|
|
162
|
-
# Use local PHP binary
|
|
163
|
-
parser = Parser(php_binary_path=Path("/usr/local/bin/php"))
|
|
164
|
-
ast = parser.parse("<?php echo 'hello';")
|
|
165
|
-
|
|
166
|
-
# Or download from remote URL
|
|
167
|
-
parser = Parser(php_binary_url="https://example.com/php-8.4.zip")
|
|
168
|
-
ast = parser.parse("<?php echo 'hello';")
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
## API Reference
|
|
172
|
-
|
|
173
|
-
### Main Functions
|
|
174
|
-
|
|
175
|
-
- **`parse_code(code: str) -> list[Node]`**: Parse PHP code string into raw statement nodes (no project/file structure)
|
|
176
|
-
- **`parse(code: str) -> AST`**: Parse PHP code string and return AST with project/file structure (backward compatibility)
|
|
177
|
-
- **`parse_file(path: str) -> AST`**: Parse a single PHP file and return AST with project/file structure
|
|
178
|
-
- **`parse_project(project_path: str, file_filter: Callable[[Path], bool] = lambda p: p.suffix == '.php') -> AST`**: Parse all PHP files in a project directory into a single AST with project structure. `file_filter` function to customize which files are included (defaults to `.php` suffix check)
|
|
179
|
-
|
|
180
|
-
### Classes
|
|
181
|
-
|
|
182
|
-
- **`AST`**: Represents the complete AST as a graph
|
|
183
|
-
- `node(id)`: Get node by ID
|
|
184
|
-
- `nodes(predicate)`: Iterate nodes matching predicate
|
|
185
|
-
- `first_node(predicate)`: Get first matching node
|
|
186
|
-
- `succ(node, predicate)`: Get successor nodes (children via PARENT_OF edges)
|
|
187
|
-
- `prev(node, predicate)`: Get predecessor nodes (parents via PARENT_OF edges)
|
|
188
|
-
- `descendants(node, predicate)`: Get all descendants via BFS
|
|
189
|
-
- `ancestors(node, predicate)`: Get all ancestors via BFS
|
|
190
|
-
- `root_node`: Property returning the root/project node
|
|
191
|
-
- `project_node`: Property returning the project node (alias for root_node)
|
|
192
|
-
- `files()`: Get all file nodes in the project
|
|
193
|
-
- `get_file(node_id)`: Get the file node containing the given node
|
|
194
|
-
- `to_json()`: Reconstruct PHP-Parser JSON
|
|
195
|
-
|
|
196
|
-
- **`Node`**: Wraps a single AST node
|
|
197
|
-
- `id`: Node identifier
|
|
198
|
-
- `node_type`: Node type (e.g., "Stmt_Function") - returns `nodeType` property
|
|
199
|
-
- `all_properties`: All node properties as dict
|
|
200
|
-
- `get_property(*names)`: Get property value
|
|
201
|
-
- `start_line`, `end_line`: Line number properties
|
|
202
|
-
- `__getitem__(key)`: Dict-like access (e.g., `node["name"]`)
|
|
203
|
-
- `get(key, default)`: Get property with default value
|
|
204
|
-
|
|
205
|
-
- **`Parser`**: Parses PHP code
|
|
206
|
-
- `__init__(php_binary_path=None, php_binary_url=None)`: Initialize parser
|
|
207
|
-
- `php_binary_path`: Optional path to local PHP binary
|
|
208
|
-
- `php_binary_url`: Optional URL to download PHP binary from
|
|
209
|
-
- `parse_code(code)`: Parse code string into list of nodes
|
|
210
|
-
- `parse(code)`: Parse code string into AST (backward compatibility)
|
|
211
|
-
- `parse_file(path)`: Parse file into AST with project/file structure
|
|
212
|
-
- `parse_project(project_path)`: Parse project directory into AST with project structure
|
|
213
|
-
|
|
214
|
-
- **`PrettyPrinter`**: Generates PHP code
|
|
215
|
-
- `__init__(php_binary_path=None, php_binary_url=None)`: Initialize printer
|
|
216
|
-
- `php_binary_path`: Optional path to local PHP binary
|
|
217
|
-
- `php_binary_url`: Optional URL to download PHP binary from
|
|
218
|
-
- `print(ast) -> dict[str, str]`: Generate code from AST, returns dictionary mapping file paths to generated code
|
|
219
|
-
- Returns: `{file_path: code_string, ...}` for each file in the AST
|
|
220
|
-
- If AST has no file structure, returns `{"": code_string}`
|
|
221
|
-
|
|
222
|
-
### Exceptions
|
|
223
|
-
|
|
224
|
-
- **`ParseError`**: Raised when PHP-Parser reports syntax error
|
|
225
|
-
- **`RunnerError`**: Raised when PHP execution fails
|
|
226
|
-
|
|
227
|
-
## AST Structure
|
|
228
|
-
|
|
229
|
-
The library supports three parsing modes:
|
|
230
|
-
|
|
231
|
-
1. **Code Parsing** (`parse_code`): Returns flat list of statement nodes
|
|
232
|
-
- Node IDs: `node_1`, `node_2`, ...
|
|
233
|
-
- No project/file structure
|
|
234
|
-
|
|
235
|
-
2. **File Parsing** (`parse_file`): Creates project → file → statements hierarchy
|
|
236
|
-
- Project node: `"project"` (fixed ID)
|
|
237
|
-
- File node: 8-character hex hash (e.g., `"a1b2c3d4"`)
|
|
238
|
-
- Statement nodes: `{hex}_1`, `{hex}_2`, ...
|
|
239
|
-
|
|
240
|
-
3. **Project Parsing** (`parse_project`): Creates project → multiple files → statements hierarchy
|
|
241
|
-
- Single project node: `"project"` (fixed ID)
|
|
242
|
-
- Multiple file nodes: Each with unique hex hash
|
|
243
|
-
- Statement nodes: Prefixed with their file's hash
|
|
244
|
-
|
|
245
|
-
## Architecture
|
|
246
|
-
|
|
247
|
-
The library follows a delegation-based architecture:
|
|
248
|
-
|
|
249
|
-
1. **PHP-Parser** handles all parsing and code generation
|
|
250
|
-
2. **cpg2py Storage** stores the AST as a graph
|
|
251
|
-
3. **Python classes** provide convenient access to the graph
|
|
252
|
-
|
|
253
|
-
This design ensures:
|
|
254
|
-
- No need to maintain PHP node type definitions in Python
|
|
255
|
-
- Automatic compatibility with PHP-Parser updates
|
|
256
|
-
- Full access to PHP-Parser's features
|
|
257
|
-
- Support for multi-file project analysis with merge capabilities
|
|
258
|
-
|
|
259
|
-
## Requirements
|
|
260
|
-
|
|
261
|
-
- Python >= 3.11
|
|
262
|
-
- cpg2py
|
|
263
|
-
- static-php-py (bundled PHP binaries)
|
|
264
|
-
|
|
265
|
-
## License
|
|
266
|
-
|
|
267
|
-
MIT License - see LICENSE file for details.
|
|
268
|
-
|
|
269
|
-
## Documentation
|
|
270
|
-
|
|
271
|
-
For detailed design documentation, see:
|
|
272
|
-
- [Design Document](docs/design.md)
|
|
273
|
-
- [Idea Document](docs/idea.md)
|
|
274
|
-
- [API Demo](docs/api_demo.ipynb)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{php_parser_py-1.1.1 → php_parser_py-1.2.0}/src/php_parser_py/resources/php-parser-4.19.4.zip
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|