-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathPHPFile.php
More file actions
199 lines (170 loc) · 5.47 KB
/
PHPFile.php
File metadata and controls
199 lines (170 loc) · 5.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
<?php
namespace DrupalCodeBuilder\Generator;
use DrupalCodeBuilder\Generator\FormattingTrait\PHPFormattingTrait;
use DrupalCodeBuilder\Generator\Render\DocBlock;
/**
* Generator for general PHP code files.
*
* Code files for modules, theme, etc, should inherit from this.
*/
abstract class PHPFile extends File {
use PHPFormattingTrait;
use NameFormattingTrait;
/**
* An array of functions for this file.
*
* TODO: Remove this.
*
* @see code_body()
*/
protected $functions = [];
/**
* Return the contents of the file.
*
* Helper for subclasses. Serves to concatenate standard pieces of the file.
*
* @return
* An array of text strings, in the correct order for concatenation.
*/
protected function fileContents() {
// If only bare code is requested, only output the body, wthout headers
// or footer.
/*
// TODO: Decide whether to restore this or remove it.
$module_data = $this->root_component->component_data;
if (!empty($module_data['bare_code'])) {
return $this->phpCodeBody();
}
*/
// File contents are built up.
$file_contents = array_merge(
$this->file_header(),
// The code header and body are themselves arrays.
$this->code_header(),
$this->phpCodeBody()
);
if (!empty($this->code_footer())) {
$file_contents[] = $this->code_footer();
}
return $file_contents;
}
/**
* Return the PHP file header lines.
*/
function file_header() {
return [
"<?php",
'',
];
}
/**
* Return the file doxygen header and any custom header code.
*/
function code_header() {
$docblock = DocBlock::file();
$docblock[] = $this->fileDocblockSummary();
$code = $docblock->render();
// Blank line after the file docblock.
$code[] = '';
return $code;
}
/**
* Return the main body of the file code.
*
* This is everything after the opening '<php' tag.
*
* @return
* An array of code lines. Keys are immaterial but should avoid clashing.
*/
abstract function phpCodeBody();
/**
* Remove fully-qualified classnames, extracting them to an array.
*
* It is assummed that %-style tokens are *not* used in the fully qualified
* classname, as the extracted values will be used for sorting the imports
* section of the file; @see self::imports().
*
* @param &$class_code
* An array of PHP code lines to work on. All namespaced classes will be
* replaced with plain classes.
* @param &$imported_classes
* An array to populate with the fully-qualified classnames which are
* removed. These are without the initial namespace separator.
* @param string $current_namespace
* (optional) The namespace of the current file, without the initial '\'. If
* omitted, no comparison of namespace is done.
*/
protected function extractFullyQualifiedClasses(&$class_code, &$imported_classes, $current_namespace = '') {
$current_namespace_pieces = explode('\\', $current_namespace);
foreach ($class_code as &$line) {
// Skip lines which are part of a comment block.
if (preg_match('@^\s*\*@', $line)) {
continue;
}
// Skip lines which are a single comment.
if (preg_match('@^\s*//@', $line)) {
continue;
}
// Skip PHPStorm variable typehints.
if (preg_match('@^\s*/\*\*@', $line)) {
continue;
}
$matches = [];
// Do not match after a ' or ", as then the class name is a quoted string
// and should be left alone.
// Do not match after a letter or number, as then that's also part of the
// namespace and we shouldn't be matching only the tail end.
if (preg_match_all('@(?<![\'"[:alnum:]])(?:\\\\(\w+)){2,}@', $line, $matches, PREG_SET_ORDER)) {
foreach ($matches as $match_set) {
$fully_qualified_class_name = $match_set[0];
$class_name = $match_set[1];
$line = preg_replace('@' . preg_quote($fully_qualified_class_name) . '@', $class_name, $line);
$fully_qualified_class_name = ltrim($fully_qualified_class_name, '\\');
$namespace_pieces = array_slice(explode('\\', $fully_qualified_class_name), 0, -1);
if ($namespace_pieces != $current_namespace_pieces) {
$imported_classes[] = ltrim($fully_qualified_class_name, '\\');
}
}
}
}
// Remove duplicates.
$imported_classes = array_unique($imported_classes);
}
/**
* Produces the namespace import statements.
*
* @param $imported_classes
* (optional) An array of fully-qualified class names. The presence of the
* leading slash is immaterial. Duplicates are removed.
*/
function imports($imported_classes = []) {
$imports = [];
if ($imported_classes) {
foreach ($imported_classes as $fully_qualified_class_name) {
$fully_qualified_class_name = ltrim($fully_qualified_class_name, '\\');
$imports[] = "use $fully_qualified_class_name;";
}
// Sort the imported classes.
sort($imports);
// Remove duplicates.
$imports = array_unique($imports);
$imports[] = '';
}
return $imports;
}
/**
* Returns the summary line for the file docblock.
*
* @return
* The text to go after the @file tag in the file's docblock.
*/
function fileDocblockSummary() {
return "TODO: Enter file description here.";
}
/**
* Returns a file footer.
*
* This is only used if non-empty.
*/
function code_footer() {}
}