Skip to content

Commit 81e9d8f

Browse files
committed
Merge branch 'release/0.3'
2 parents e3d5897 + a840249 commit 81e9d8f

14 files changed

+488
-81
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
vendor
12
composer.lock
23
composer.phar
3-
vendor
4+
tests/*.expect
5+
tests/*.result

ChangeLog.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
Version 0.3
2+
-----------
3+
4+
* able to control the memroy size to be used
5+
thanks to Dannel Jurado
6+
* improved command line compatiblity to ctags
7+
thanks to Sander Marechal
8+
* bug fixes: #5 and #7
9+
110
Version 0.2
211
-----------
312

PHPCtags.class.php

Lines changed: 176 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,100 @@ class PHPCtags
33
{
44
private $mFile;
55

6-
private $mParser;
6+
private $mFiles;
77

88
private static $mKinds = array(
9-
'c' => 'class',
10-
'm' => 'method',
11-
'f' => 'function',
12-
'p' => 'property',
13-
'd' => 'constant',
14-
'v' => 'variable',
15-
'i' => 'interface',
16-
);
17-
18-
public function __construct()
9+
'c' => 'class',
10+
'm' => 'method',
11+
'f' => 'function',
12+
'p' => 'property',
13+
'd' => 'constant',
14+
'v' => 'variable',
15+
'i' => 'interface',
16+
);
17+
18+
private $mParser;
19+
20+
private $mStructs;
21+
22+
private $mOptions;
23+
24+
public function __construct($options)
1925
{
2026
$this->mParser = new PHPParser_Parser(new PHPParser_Lexer);
27+
$this->mStructs = array();
28+
$this->mOptions = $options;
29+
}
30+
31+
public function setMFile($file)
32+
{
33+
if (empty($file)) {
34+
throw new PHPCtagsException('No File specified.');
35+
}
36+
37+
if (!file_exists($file)) {
38+
throw new PHPCtagsException('Warning: cannot open source file "' . $file . '" : No such file');
39+
}
40+
41+
if (!is_readable($file)) {
42+
throw new PHPCtagsException('Warning: cannot open source file "' . $file . '" : File is not readable');
43+
}
44+
45+
$this->mFile = realpath($file);
2146
}
2247

2348
public static function getMKinds()
2449
{
2550
return self::$mKinds;
2651
}
2752

53+
public function addFile($file)
54+
{
55+
$this->mFiles[realpath($file)] = 1;
56+
}
57+
58+
public function addFiles($files)
59+
{
60+
foreach ($files as $file) {
61+
$this->addFile($file);
62+
}
63+
}
64+
2865
private function getNodeAccess($node)
2966
{
3067
if ($node->isPrivate()) return 'private';
3168
if ($node->isProtected()) return 'protected';
3269
return 'public';
3370
}
3471

35-
private static function helperSortByLine($a, $b) {
72+
/**
73+
* stringSortByLine
74+
*
75+
* Sort a string based on its line delimiter
76+
*
77+
* @author Techlive Zheng
78+
*
79+
* @access public
80+
* @static
81+
*
82+
* @param string $str string to be sorted
83+
* @param boolean $foldcse case-insensitive sorting
84+
*
85+
* @return string sorted string
86+
**/
87+
public static function stringSortByLine($str, $foldcase=FALSE)
88+
{
89+
$arr = explode("\n", $str);
90+
if (!$foldcase)
91+
sort($arr, SORT_STRING);
92+
else
93+
sort($arr, SORT_STRING | SORT_FLAG_CASE);
94+
$str = implode("\n", $arr);
95+
return $str;
96+
}
97+
98+
private static function helperSortByLine($a, $b)
99+
{
36100
return $a['line'] > $b['line'] ? 1 : 0;
37101
}
38102

@@ -47,7 +111,7 @@ private function struct($node, $reset=FALSE, $parent=array())
47111

48112
$kind = $name = $line = $access = '';
49113

50-
if(!empty($parent)) array_push($scope, $parent);
114+
if (!empty($parent)) array_push($scope, $parent);
51115

52116
if (is_array($node)) {
53117
foreach ($node as $subNode) {
@@ -79,6 +143,10 @@ private function struct($node, $reset=FALSE, $parent=array())
79143
foreach ($node as $subNode) {
80144
$this->struct($subNode, FALSE, array('method' => $name));
81145
}
146+
} elseif ($node instanceof PHPParser_Node_Stmt_If) {
147+
foreach ($node as $subNode) {
148+
$this->struct($subNode);
149+
}
82150
} elseif ($node instanceof PHPParser_Node_Stmt_Const) {
83151
$kind = 'd';
84152
$cons = $node->consts[0];
@@ -93,15 +161,17 @@ private function struct($node, $reset=FALSE, $parent=array())
93161
//@todo
94162
} elseif ($node instanceof PHPParser_Node_Stmt_Declare) {
95163
//@todo
164+
} elseif ($node instanceof PHPParser_Node_Stmt_TryCatch) {
165+
foreach ($node as $subNode) {
166+
$this->struct($subNode);
167+
}
96168
} elseif ($node instanceof PHPParser_Node_Stmt_Function) {
97169
$kind = 'f';
98170
$name = $node->name;
99171
$line = $node->getLine();
100172
foreach ($node as $subNode) {
101173
$this->struct($subNode, FALSE, array('function' => $name));
102174
}
103-
} elseif ($node instanceof PHPParser_Node_Stmt_Trait) {
104-
//@todo
105175
} elseif ($node instanceof PHPParser_Node_Stmt_Interface) {
106176
$kind = 'i';
107177
$name = $node->name;
@@ -115,7 +185,7 @@ private function struct($node, $reset=FALSE, $parent=array())
115185
$this->struct($subNode);
116186
}
117187
} elseif ($node instanceof PHPParser_Node_Expr_Assign) {
118-
if(is_string($node->var->name)) {
188+
if (is_string($node->var->name)) {
119189
$kind = 'v';
120190
$node = $node->var;
121191
$name = $node->name;
@@ -136,6 +206,7 @@ private function struct($node, $reset=FALSE, $parent=array())
136206

137207
if (!empty($kind) && !empty($name) && !empty($line)) {
138208
$structs[] = array(
209+
'file' => $this->mFile,
139210
'kind' => $kind,
140211
'name' => $name,
141212
'line' => $line,
@@ -144,66 +215,71 @@ private function struct($node, $reset=FALSE, $parent=array())
144215
);
145216
}
146217

147-
if(!empty($parent)) array_pop($scope);
218+
if (!empty($parent)) array_pop($scope);
148219

149-
usort($structs, 'self::helperSortByLine');
220+
// if no --sort is given, sort by occurrence
221+
if (!isset($this->mOptions['sort']) || $this->mOptions['sort'] == 'no') {
222+
usort($structs, 'self::helperSortByLine');
223+
}
150224

151225
return $structs;
152226
}
153227

154-
private function render($structs, $options)
228+
private function render()
155229
{
156230
$str = '';
157-
$lines = file($this->mFile);
158-
foreach ($structs as $struct) {
231+
foreach ($this->mStructs as $struct) {
232+
$file = $struct['file'];
233+
234+
if (!isset($files[$file]))
235+
$files[$file] = file($file);
236+
237+
$lines = $files[$file];
238+
159239
if (empty($struct['name']) || empty($struct['line']) || empty($struct['kind']))
160240
return;
161241

162-
if ($struct['kind'] == 'v') {
163-
$str .= "$" . $struct['name'];
164-
} else {
165-
$str .= $struct['name'];
166-
}
242+
$str .= $struct['name'];
167243

168-
$str .= "\t" . $this->mFile;
244+
$str .= "\t" . $file;
169245

170-
if ($options['excmd'] == 'number') {
246+
if ($this->mOptions['excmd'] == 'number') {
171247
$str .= "\t" . $struct['line'];
172248
} else { //excmd == 'mixed' or 'pattern', default behavior
173249
$str .= "\t" . "/^" . rtrim($lines[$struct['line'] - 1], "\n") . "$/";
174250
}
175251

176-
if ($options['format'] == 1) {
252+
if ($this->mOptions['format'] == 1) {
177253
$str .= "\n";
178254
continue;
179255
}
180256

181257
$str .= ";\"";
182258

183259
#field=k, kind of tag as single letter
184-
if (in_array('k', $options['fields'])) {
185-
in_array('z', $options['fields']) && $str .= "kind:";
260+
if (in_array('k', $this->mOptions['fields'])) {
261+
in_array('z', $this->mOptions['fields']) && $str .= "kind:";
186262
$str .= "\t" . $struct['kind'];
187263
} else
188264
#field=K, kind of tag as fullname
189-
if (in_array('K', $options['fields'])) {
190-
in_array('z', $options['fields']) && $str .= "kind:";
265+
if (in_array('K', $this->mOptions['fields'])) {
266+
in_array('z', $this->mOptions['fields']) && $str .= "kind:";
191267
$str .= "\t" . self::$mKinds[$struct['kind']];
192268
}
193269

194270
#field=n
195-
if (in_array('n', $options['fields'])) {
271+
if (in_array('n', $this->mOptions['fields'])) {
196272
$str .= "\t" . "line:" . $struct['line'];
197273
}
198274

199275
#field=s
200-
if (in_array('s', $options['fields']) && !empty($struct['scope'])) {
276+
if (in_array('s', $this->mOptions['fields']) && !empty($struct['scope'])) {
201277
$scope = array_pop($struct['scope']);
202-
list($type,$name) = each($scope);
278+
list($type, $name) = each($scope);
203279
switch ($type) {
204280
case 'method':
205281
$scope = array_pop($struct['scope']);
206-
list($p_type,$p_name) = each($scope);
282+
list($p_type, $p_name) = each($scope);
207283
$scope = 'method:' . $p_name . '::' . $name;
208284
break;
209285
default:
@@ -214,20 +290,77 @@ private function render($structs, $options)
214290
}
215291

216292
#field=a
217-
if (in_array('a', $options['fields']) && !empty($struct['access'])) {
293+
if (in_array('a', $this->mOptions['fields']) && !empty($struct['access'])) {
218294
$str .= "\t" . "access:" . $struct['access'];
219295
}
220296

221297
$str .= "\n";
222298
}
299+
300+
// remove the last line ending
301+
$str = trim($str);
302+
303+
// sort the result as instructed
304+
if (isset($this->mOptions['sort']) && ($this->mOptions['sort'] == 'yes' || $this->mOptions['sort'] == 'foldcase')) {
305+
$str = self::stringSortByLine($str, $this->mOptions['sort'] == 'foldcase');
306+
}
307+
223308
return $str;
224309
}
225310

226-
public function export($file, $options)
311+
public function export()
312+
{
313+
if (empty($this->mFiles)) {
314+
throw new PHPCtagsException('No File specified.');
315+
}
316+
317+
foreach (array_keys($this->mFiles) as $file) {
318+
$this->process($file);
319+
}
320+
321+
return $this->render();
322+
}
323+
324+
private function process($file)
227325
{
228-
//@todo Check for existence
229-
$this->mFile = $file;
230-
$structs = $this->struct($this->mParser->parse(file_get_contents($this->mFile)), TRUE);
231-
echo $this->render($structs, $options);
326+
if (is_dir($file) && isset($this->mOptions['R'])) {
327+
$iterator = new RecursiveIteratorIterator(
328+
new RecursiveDirectoryIterator(
329+
$file,
330+
FilesystemIterator::SKIP_DOTS |
331+
FilesystemIterator::FOLLOW_SYMLINKS
332+
)
333+
);
334+
335+
$extensions = array('.php', '.php3', '.php4', '.php5', '.phps');
336+
337+
foreach ($iterator as $filename) {
338+
if (!in_array(substr($filename, strrpos($filename, '.')), $extensions)) {
339+
continue;
340+
}
341+
342+
if (isset($this->mOptions['exclude']) && false !== strpos($filename, $this->mOptions['exclude'])) {
343+
continue;
344+
}
345+
346+
$this->setMFile((string) $filename);
347+
$this->mStructs = array_merge(
348+
$this->mStructs,
349+
$this->struct($this->mParser->parse(file_get_contents($this->mFile)), TRUE)
350+
);
351+
}
352+
} else {
353+
$this->setMFile($file);
354+
$this->mStructs = array_merge(
355+
$this->mStructs,
356+
$this->struct($this->mParser->parse(file_get_contents($this->mFile)), TRUE)
357+
);
358+
}
359+
}
360+
}
361+
362+
class PHPCtagsException extends Exception {
363+
public function __toString() {
364+
return "PHPCtags: {$this->message}\n";
232365
}
233366
}

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,5 @@ Acknowledgements
3838
----------------
3939

4040
* [Snapi](https://github.com/sanpii) for composer support.
41+
* [DeMarko](https://github.com/DeMarko) for memory limit support.
42+
* [Sander Marechal](https://github.com/sandermarechal) for improve console support

0 commit comments

Comments
 (0)