php-parser-py 1.0.0__tar.gz → 1.1.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: php-parser-py
3
- Version: 1.0.0
3
+ Version: 1.1.0
4
4
  Summary: Python wrapper for PHP-Parser using cpg2py
5
5
  Author: samhsu-dev
6
6
  Author-email: samhsu-dev <yxu166@jh.edu>
@@ -52,7 +52,7 @@ nodes = parse_code(code)
52
52
  # Returns list of Node instances (no project/file structure)
53
53
 
54
54
  for node in nodes:
55
- print(f"Node type: {node.label}")
55
+ print(f"Node type: {node.node_type}")
56
56
  ```
57
57
 
58
58
  ### Parse PHP File (With Project Structure)
@@ -69,7 +69,7 @@ files = ast.files()
69
69
  print(f"Project has {len(files)} file(s)")
70
70
 
71
71
  # Get file containing a specific node
72
- stmt_node = ast.first_node(lambda n: n.label == "Stmt_Function")
72
+ stmt_node = ast.first_node(lambda n: n.node_type == "Stmt_Function")
73
73
  file_node = ast.get_file(stmt_node.id)
74
74
  print(f"Function is in file: {file_node.get_property('filePath')}")
75
75
  ```
@@ -86,8 +86,8 @@ ast = parse_project(["file1.php", "file2.php", "file3.php"])
86
86
  for file_node in ast.files():
87
87
  print(f"File: {file_node.get_property('filePath')}")
88
88
  # Get statements in this file
89
- for stmt in ast.children(file_node):
90
- print(f" Statement: {stmt.label}")
89
+ for stmt in ast.succ(file_node):
90
+ print(f" Statement: {stmt.node_type}")
91
91
 
92
92
  # Access project and file paths
93
93
  ast = parse_project(["file1.php", "subdir/file2.php"])
@@ -107,19 +107,19 @@ from php_parser_py import parse_file
107
107
  ast = parse_file("example.php")
108
108
 
109
109
  # Find all echo statements
110
- for echo in ast.nodes(lambda n: n.label == "Stmt_Echo"):
111
- print(f"Echo at line {echo.get_property('startLine')}")
110
+ for echo in ast.nodes(lambda n: n.node_type == "Stmt_Echo"):
111
+ print(f"Echo at line {echo.start_line}")
112
112
 
113
113
  # Get children of a node (via PARENT_OF edges)
114
114
  for child in ast.succ(some_node):
115
- print(f"Child type: {child.label}")
115
+ print(f"Child type: {child.node_type}")
116
116
 
117
117
  # Get all descendants
118
118
  for descendant in ast.descendants(some_node):
119
- print(f"Descendant: {descendant.label}")
119
+ print(f"Descendant: {descendant.node_type}")
120
120
 
121
121
  # Get file containing a node
122
- stmt_node = ast.first_node(lambda n: n.label == "Stmt_Function")
122
+ stmt_node = ast.first_node(lambda n: n.node_type == "Stmt_Function")
123
123
  file_node = ast.get_file(stmt_node.id)
124
124
  if file_node:
125
125
  print(f"Function is in: {file_node.get_property('filePath')}")
@@ -200,10 +200,12 @@ ast = parser.parse("<?php echo 'hello';")
200
200
 
201
201
  - **`Node`**: Wraps a single AST node
202
202
  - `id`: Node identifier
203
- - `label`: Node type (e.g., "Stmt_Function")
204
- - `node_type`: Alias for label (PHP-Parser node type)
205
- - `properties`: All node properties
203
+ - `node_type`: Node type (e.g., "Stmt_Function") - returns `nodeType` property
204
+ - `all_properties`: All node properties as dict
206
205
  - `get_property(*names)`: Get property value
206
+ - `start_line`, `end_line`: Line number properties
207
+ - `__getitem__(key)`: Dict-like access (e.g., `node["name"]`)
208
+ - `get(key, default)`: Get property with default value
207
209
 
208
210
  - **`Parser`**: Parses PHP code
209
211
  - `__init__(php_binary_path=None, php_binary_url=None)`: Initialize parser
@@ -41,7 +41,7 @@ nodes = parse_code(code)
41
41
  # Returns list of Node instances (no project/file structure)
42
42
 
43
43
  for node in nodes:
44
- print(f"Node type: {node.label}")
44
+ print(f"Node type: {node.node_type}")
45
45
  ```
46
46
 
47
47
  ### Parse PHP File (With Project Structure)
@@ -58,7 +58,7 @@ files = ast.files()
58
58
  print(f"Project has {len(files)} file(s)")
59
59
 
60
60
  # Get file containing a specific node
61
- stmt_node = ast.first_node(lambda n: n.label == "Stmt_Function")
61
+ stmt_node = ast.first_node(lambda n: n.node_type == "Stmt_Function")
62
62
  file_node = ast.get_file(stmt_node.id)
63
63
  print(f"Function is in file: {file_node.get_property('filePath')}")
64
64
  ```
@@ -75,8 +75,8 @@ ast = parse_project(["file1.php", "file2.php", "file3.php"])
75
75
  for file_node in ast.files():
76
76
  print(f"File: {file_node.get_property('filePath')}")
77
77
  # Get statements in this file
78
- for stmt in ast.children(file_node):
79
- print(f" Statement: {stmt.label}")
78
+ for stmt in ast.succ(file_node):
79
+ print(f" Statement: {stmt.node_type}")
80
80
 
81
81
  # Access project and file paths
82
82
  ast = parse_project(["file1.php", "subdir/file2.php"])
@@ -96,19 +96,19 @@ from php_parser_py import parse_file
96
96
  ast = parse_file("example.php")
97
97
 
98
98
  # Find all echo statements
99
- for echo in ast.nodes(lambda n: n.label == "Stmt_Echo"):
100
- print(f"Echo at line {echo.get_property('startLine')}")
99
+ for echo in ast.nodes(lambda n: n.node_type == "Stmt_Echo"):
100
+ print(f"Echo at line {echo.start_line}")
101
101
 
102
102
  # Get children of a node (via PARENT_OF edges)
103
103
  for child in ast.succ(some_node):
104
- print(f"Child type: {child.label}")
104
+ print(f"Child type: {child.node_type}")
105
105
 
106
106
  # Get all descendants
107
107
  for descendant in ast.descendants(some_node):
108
- print(f"Descendant: {descendant.label}")
108
+ print(f"Descendant: {descendant.node_type}")
109
109
 
110
110
  # Get file containing a node
111
- stmt_node = ast.first_node(lambda n: n.label == "Stmt_Function")
111
+ stmt_node = ast.first_node(lambda n: n.node_type == "Stmt_Function")
112
112
  file_node = ast.get_file(stmt_node.id)
113
113
  if file_node:
114
114
  print(f"Function is in: {file_node.get_property('filePath')}")
@@ -189,10 +189,12 @@ ast = parser.parse("<?php echo 'hello';")
189
189
 
190
190
  - **`Node`**: Wraps a single AST node
191
191
  - `id`: Node identifier
192
- - `label`: Node type (e.g., "Stmt_Function")
193
- - `node_type`: Alias for label (PHP-Parser node type)
194
- - `properties`: All node properties
192
+ - `node_type`: Node type (e.g., "Stmt_Function") - returns `nodeType` property
193
+ - `all_properties`: All node properties as dict
195
194
  - `get_property(*names)`: Get property value
195
+ - `start_line`, `end_line`: Line number properties
196
+ - `__getitem__(key)`: Dict-like access (e.g., `node["name"]`)
197
+ - `get(key, default)`: Get property with default value
196
198
 
197
199
  - **`Parser`**: Parses PHP code
198
200
  - `__init__(php_binary_path=None, php_binary_url=None)`: Initialize parser
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "php-parser-py"
3
- version = "1.0.0"
3
+ version = "1.1.0"
4
4
  description = "Python wrapper for PHP-Parser using cpg2py"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -213,12 +213,12 @@ class AST(AbcGraphQuerier[Node, Edge]):
213
213
  # Exclude project node if it exists
214
214
  all_nodes = set(self._storage.get_nodes())
215
215
  nodes_with_parents = set()
216
-
216
+
217
217
  for edge_id in self._storage.get_edges():
218
218
  from_id, to_id, edge_type = edge_id
219
219
  if edge_type == "PARENT_OF":
220
220
  nodes_with_parents.add(to_id)
221
-
221
+
222
222
  root_nodes = all_nodes - nodes_with_parents
223
223
  # Exclude project node if present
224
224
  root_nodes.discard(self._root_node_id)
@@ -94,7 +94,7 @@ class Parser:
94
94
  if "Syntax error" in str(e):
95
95
  raise ParseError("Syntax error in PHP code", line=1) from e
96
96
  raise
97
-
97
+
98
98
  # Normalize to list
99
99
  if not isinstance(json_data, list):
100
100
  json_data = [json_data]
@@ -44,34 +44,66 @@ class PrettyPrinter:
44
44
 
45
45
  Returns:
46
46
  Dictionary mapping file paths to generated PHP source code strings.
47
- If AST has no file structure, returns a single entry with key "code"
48
- or empty string.
47
+ If AST has no file structure, returns a single entry with key ""
48
+ as the filename.
49
49
 
50
50
  Raises:
51
51
  RunnerError: If PHP-Parser execution fails.
52
52
  """
53
53
  file_nodes = ast.files()
54
-
54
+
55
55
  if not file_nodes:
56
56
  # No file structure - export all statements as a single code block
57
57
  json_str = ast.to_json()
58
58
  code = self._runner.print(json_str)
59
59
  return {"": code}
60
-
60
+
61
61
  # Generate code for each file
62
62
  result: dict[str, str] = {}
63
63
  for file_node in file_nodes:
64
64
  file_path = file_node.get_property("filePath", "")
65
65
  file_hash = file_node.id
66
-
66
+
67
67
  # Get JSON for this file only
68
68
  json_str = ast.to_json(file_hash=file_hash)
69
-
69
+
70
70
  # Generate code
71
71
  code = self._runner.print(json_str)
72
-
72
+
73
73
  # Use file path as key, or file hash if path not available
74
74
  key = file_path if file_path else file_hash
75
75
  result[key] = code
76
-
76
+
77
77
  return result
78
+
79
+ def print_file(self, ast: AST, relative_path: str) -> str:
80
+ """Generate PHP code string for a single file selected by relative path.
81
+
82
+ This method looks up the file node whose ``path`` property matches the
83
+ given relative path (as stored on file nodes), reconstructs JSON for
84
+ that file only, and returns the generated PHP source code string.
85
+
86
+ Args:
87
+ ast: AST instance containing project and file nodes.
88
+ relative_path: Relative file path stored in the ``path`` property
89
+ of the target file node (for example, ``"src/file.php"`` or
90
+ just ``"file.php"`` for single-file projects).
91
+
92
+ Returns:
93
+ Generated PHP source code string for the specified file.
94
+
95
+ Raises:
96
+ KeyError: If no file node with the given relative path exists
97
+ in the AST.
98
+ RunnerError: If PHP-Parser execution fails.
99
+ """
100
+ # Find file node by its relative path property
101
+ for file_node in ast.files():
102
+ if file_node.get_property("path") == relative_path:
103
+ file_hash = file_node.id
104
+ json_str = ast.to_json(file_hash=file_hash)
105
+ return self._runner.print(json_str)
106
+
107
+ raise KeyError(
108
+ f"File with relative path '{relative_path}' not found in AST."
109
+ )