Skip to content

Commit abc8c4c

Browse files
author
Gianluca Arbezzano
committed
Merge pull request #49 from mr-russ/performance
Rebased performance parts of #23, passes unit tests
2 parents e20b3ba + 22c6fda commit abc8c4c

File tree

2 files changed

+114
-23
lines changed

2 files changed

+114
-23
lines changed

PHPCtags.class.php

Lines changed: 87 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,18 @@ class PHPCtags
2020
);
2121

2222
private $mParser;
23-
24-
private $mStructs;
25-
23+
private $mLines;
2624
private $mOptions;
25+
private $tagdata;
26+
private $cachefile;
27+
private $filecount;
2728

2829
public function __construct($options)
2930
{
3031
$this->mParser = new PHPParser_Parser(new PHPParser_Lexer);
31-
$this->mStructs = array();
32+
$this->mLines = array();
3233
$this->mOptions = $options;
34+
$this->filecount = 0;
3335
}
3436

3537
public function setMFile($file)
@@ -59,6 +61,10 @@ public function addFile($file)
5961
$this->mFiles[realpath($file)] = 1;
6062
}
6163

64+
public function setCacheFile($file) {
65+
$this->cachefile = $file;
66+
}
67+
6268
public function addFiles($files)
6369
{
6470
foreach ($files as $file) {
@@ -250,10 +256,10 @@ private function struct($node, $reset=FALSE, $parent=array())
250256
return $structs;
251257
}
252258

253-
private function render()
259+
private function render($structure)
254260
{
255261
$str = '';
256-
foreach ($this->mStructs as $struct) {
262+
foreach ($structure as $struct) {
257263
$file = $struct['file'];
258264

259265
if (!in_array($struct['kind'], $this->mOptions['kinds'])) {
@@ -361,19 +367,42 @@ private function render()
361367
$str .= "\n";
362368
}
363369

364-
// remove the last line ending
365-
$str = trim($str);
370+
// remove the last line ending and carriage return
371+
$str = trim(str_replace("\x0D", "", $str));
372+
373+
return $str;
374+
}
375+
376+
private function full_render() {
377+
// Files will have been rendered already, just join and export.
378+
379+
$str = '';
380+
foreach($this->mLines as $file => $data) {
381+
$str .= $data.PHP_EOL;
382+
}
366383

367384
// sort the result as instructed
368385
if (isset($this->mOptions['sort']) && ($this->mOptions['sort'] == 'yes' || $this->mOptions['sort'] == 'foldcase')) {
369386
$str = self::stringSortByLine($str, $this->mOptions['sort'] == 'foldcase');
370387
}
371388

389+
// Save all tag information to a file for faster updates if a cache file was specified.
390+
if (isset($this->cachefile)) {
391+
file_put_contents($this->cachefile, serialize($this->tagdata));
392+
if ($this->mOptions['v']) {
393+
echo "Saved cache file.".PHP_EOL;
394+
}
395+
}
396+
397+
$str = trim($str);
398+
372399
return $str;
373400
}
374401

375402
public function export()
376403
{
404+
$start = microtime(true);
405+
377406
if (empty($this->mFiles)) {
378407
throw new PHPCtagsException('No File specified.');
379408
}
@@ -382,11 +411,27 @@ public function export()
382411
$this->process($file);
383412
}
384413

385-
return $this->render();
414+
$content = $this->full_render();
415+
416+
$end = microtime(true);
417+
418+
if ($this->mOptions['V']) {
419+
echo "It tooks ".($end-$start)." seconds.".PHP_EOL;
420+
}
421+
422+
return $content;
386423
}
387424

388425
private function process($file)
389426
{
427+
// Load the tag md5 data to skip unchanged files.
428+
if (!isset($this->tagdata) && isset($this->cachefile) && file_exists(realpath($this->cachefile))) {
429+
if ($this->mOptions['v']) {
430+
echo "Loaded cache file.".PHP_EOL;
431+
}
432+
$this->tagdata = unserialize(file_get_contents(realpath($this->cachefile)));
433+
}
434+
390435
if (is_dir($file) && isset($this->mOptions['R'])) {
391436
$iterator = new RecursiveIteratorIterator(
392437
new ReadableRecursiveDirectoryIterator(
@@ -408,32 +453,54 @@ private function process($file)
408453
}
409454

410455
try {
411-
$this->setMFile((string) $filename);
412-
$this->mStructs = array_merge(
413-
$this->mStructs,
414-
$this->struct($this->mParser->parse(file_get_contents($this->mFile)), TRUE)
415-
);
456+
$this->process_single_file($filename);
416457
} catch(Exception $e) {
417458
echo "PHPParser: {$e->getMessage()} - {$filename}".PHP_EOL;
418459
}
419460
}
420461
} else {
421462
try {
422-
$this->setMFile($file);
423-
$this->mStructs = array_merge(
424-
$this->mStructs,
425-
$this->struct($this->mParser->parse(file_get_contents($this->mFile)), TRUE)
426-
);
463+
$this->process_single_file($file);
427464
} catch(Exception $e) {
428465
echo "PHPParser: {$e->getMessage()} - {$file}".PHP_EOL;
429466
}
430467
}
431468
}
469+
470+
private function process_single_file($filename)
471+
{
472+
if ($this->mOptions['v'] && $this->filecount > 1 && $this->filecount % 64 == 0) {
473+
echo " ".$this->filecount." files".PHP_EOL;
474+
}
475+
$this->filecount++;
476+
477+
$this->setMFile((string) $filename);
478+
$file = file_get_contents($this->mFile);
479+
$md5 = md5($file);
480+
if (isset($this->tagdata[$this->mFile][$md5])) {
481+
// The file is the same as the previous time we analyzed and saved.
482+
$this->mLines[$this->mFile] = $this->tagdata[$this->mFile][$md5];
483+
if ($this->mOptions['V']) {
484+
echo ".";
485+
}
486+
return;
487+
}
488+
489+
$struct = $this->struct($this->mParser->parse($file), TRUE);
490+
$this->mLines[$this->mFile] = $this->render($struct);
491+
$this->tagdata[$this->mFile][$md5] = $this->mLines[$this->mFile];
492+
if ($this->mOptions['debug']) {
493+
echo "Parse: ".($finishfile - $startfile).", Merge: ".($finishmerge-$finishfile)."; (".$this->filecount.")".$this->mFile.PHP_EOL;
494+
} else if ($this->mOptions['v']) {
495+
echo "U";
496+
}
497+
}
498+
432499
}
433500

434501
class PHPCtagsException extends Exception {
435502
public function __toString() {
436-
return "PHPCtags: {$this->message}\n";
503+
return "\nPHPCtags: {$this->message}\n";
437504
}
438505
}
439506

bootstrap.php

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
Addresses: <techlivezheng@gmail.com>, https://github.com/techlivezheng/phpctags
1919
EOF;
2020

21-
$options = getopt('af:Nno:RuV', array(
21+
$options = getopt('aC:f:Nno:RuV', array(
2222
'append::',
2323
'debug',
2424
'exclude:',
@@ -29,6 +29,7 @@
2929
'help',
3030
'recurse::',
3131
'sort::',
32+
'verbose::',
3233
'version',
3334
'memory::',
3435
));
@@ -42,12 +43,19 @@
4243
-f <name>
4344
Write tags to specified file. Value of "-" writes tags to stdout
4445
["tags"].
46+
-C <name>
47+
Use a cache file to store tags for faster updates.
4548
-n Equivalent to --excmd=number.
4649
-N Equivalent to --excmd=pattern.
4750
-o Alternative for -f.
4851
-R Equivalent to --recurse.
4952
-u Equivalent to --sort=no.
53+
<<<<<<< HEAD
54+
-v Equivalent to --verbose.
55+
-V Equivalent to --version.
56+
=======
5057
-V Equivalent to --verbose.
58+
>>>>>>> a86869b... Fixed issues based on @mr-russ's PR.
5159
--append=[yes|no]
5260
Should tags should be appended to existing tag file [no]?
5361
--debug
@@ -72,12 +80,19 @@
7280
Recurse into directories supplied on command line [no].
7381
--sort=[yes|no|foldcase]
7482
Should tags be sorted (optionally ignoring case) [yes]?.
83+
<<<<<<< HEAD
84+
--Version
85+
=======
86+
--verbose=[yes|no]
87+
Enable verbose messages describing actions on each source file.
7588
--version
89+
>>>>>>> a86869b... Fixed issues based on @mr-russ's PR.
7690
Print version identifier to standard output.
7791
EOF;
7892

7993
// prune options and its value from the $argv array
8094
$argv_ = array();
95+
8196
foreach ($options as $option => $value) {
8297
foreach ($argv as $key => $chunk) {
8398
$regex = '/^'. (isset($option[1]) ? '--' : '-') . $option . '/';
@@ -88,9 +103,17 @@
88103
}
89104
while ($key = array_pop($argv_)) unset($argv[$key]);
90105

91-
// option -V is an alternative to --version
106+
// option -V is an alternative to --verbose
92107
if (isset($options['V'])) {
93-
$options['version'] = FALSE;
108+
$options['verbose'] = 'yes';
109+
}
110+
111+
if (isset($options['verbose'])) {
112+
if ($options['verbose'] === FALSE || yes_or_no($options['verbose']) == 'yes') {
113+
$options['V'] = 'yes';
114+
} else if (yes_or_no($options['verbose']) != 'no') {
115+
die('phpctags: Invalid value for "verbose" option'.PHP_EOL);
116+
}
94117
}
95118

96119
if (!isset($options['debug'])) {
@@ -212,6 +235,7 @@
212235
try {
213236
$ctags = new PHPCtags($options);
214237
$ctags->addFiles($argv);
238+
$ctags->setCacheFile(isset($options['C']) ? $options['C'] : null);
215239
$result = $ctags->export();
216240
} catch (Exception $e) {
217241
die("phpctags: {$e->getMessage()}".PHP_EOL);

0 commit comments

Comments
 (0)