@@ -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
434501class PHPCtagsException extends Exception {
435502 public function __toString () {
436- return "PHPCtags : {$ this ->message }\n" ;
503+ return "\n PHPCtags : {$ this ->message }\n" ;
437504 }
438505}
439506
0 commit comments