1<?php 2 3use dokuwiki\plugin\cachestats\FileStatistics; 4use splitbrain\phpcli\Options; 5use splitbrain\phpcli\TableFormatter; 6 7/** 8 * DokuWiki Plugin cachestats (CLI Component) 9 * 10 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 11 * @author Andreas Gohr <andi@splitbrain.org> 12 */ 13class cli_plugin_cachestats extends \dokuwiki\Extension\CLIPlugin 14{ 15 /** @var string[] */ 16 private array $buckets = ['<1d', '<1w', '<1m', '<3m', '<6m', '<1y', '>1y']; 17 18 /** @inheritDoc */ 19 protected function setup(Options $options) 20 { 21 $options->setHelp('Collect statistics about the cache directory.'); 22 23 $options->registerOption('format', 'Output format: table|json|csv', 'f', 'table'); 24 $options->registerOption('sort', 'Sort by count|size|dups', 's', false); 25 } 26 27 /** @inheritDoc */ 28 protected function main(Options $options) 29 { 30 global $conf; 31 32 $sort = $options->getOpt('sort', 'size'); 33 if (!in_array($sort, ['count', 'size', 'dups'])) { 34 $this->error("Invalid sort option '$sort'. Allowed are: count, size, dups."); 35 return 1; 36 } 37 38 $format = $options->getOpt('format', 'table'); 39 if (!in_array($format, ['table', 'json', 'csv'])) { 40 $this->error("Invalid format option '$format'. Allowed are: table, json, csv."); 41 return 1; 42 } 43 44 if ($format === 'json') { 45 fprintf(STDERR, 'Collecting cache statistics from ' . $conf['cachedir'] . "…\n"); 46 } else { 47 $this->info('Collecting cache statistics from ' . $conf['cachedir'] . '…'); 48 } 49 50 $stats = (new FileStatistics($conf['cachedir']))->collect(); 51 52 // for debugging 53 print_r($stats); 54 55 $keys = array_unique( 56 array_merge( 57 array_keys($stats['extensions']), 58 array_keys($stats['sizes']), 59 array_keys($stats['duplicates']) 60 ) 61 ); 62 $result = []; 63 foreach ($keys as $key) { 64 $result[$key] = [ 65 'count' => $stats['extensions'][$key] ?? 0, 66 'size' => $stats['sizes'][$key] ?? 0, 67 'dups' => $stats['duplicates'][$key] ?? 0, 68 ]; 69 foreach ($this->buckets as $bucket) { 70 $result[$key][$bucket] = $stats['modified_groups'][$key][$bucket] ?? 0; 71 } 72 } 73 74 // sort with preserved keys 75 uasort($result, function ($a, $b) use ($sort) { 76 return $b[$sort] <=> $a[$sort]; 77 }); 78 79 match ($format) { 80 'json' => $this->print_json($result), 81 'csv' => $this->print_csv($result), 82 default => $this->print_table($result), 83 }; 84 return 0; 85 } 86 87 /** 88 * Output statistics as JSON 89 */ 90 private function print_json(array $result): void 91 { 92 echo json_encode($result, JSON_PRETTY_PRINT); 93 } 94 95 /** 96 * Output statistics as CSV 97 */ 98 private function print_csv(array $result): void 99 { 100 $handle = fopen('php://output', 'w'); 101 if ($handle === false) { 102 $this->error('Could not open output for CSV.'); 103 return; 104 } 105 106 $header = array_merge(['extension'], array_keys(reset($result))); 107 fputcsv($handle, $header); 108 109 foreach ($result as $ext => $data) { 110 fputcsv($handle, array_merge([$ext], $data)); 111 } 112 } 113 114 /** 115 * Output statistics as table 116 */ 117 private function print_table(array $result): void 118 { 119 $colWidth = 9; 120 121 $headers = array_merge(['ext'], array_keys(reset($result))); 122 $widths = array_merge(['*'], array_fill(0, count($headers) -1, $colWidth)); 123 $colors = array_fill(0, count($headers), ''); 124 125 // ensure terminal is wide enough, otherwise break ugly 126 $tr = new TableFormatter($this->colors); 127 if($tr->getMaxWidth() < $colWidth * count($headers)){; 128 $tr->setMaxWidth($colWidth * count($headers) + 10); 129 } 130 131 echo $tr->format($widths, $headers, $colors); 132 133 foreach ($result as $ext => $data) { 134 array_walk( 135 $data, 136 fn (&$v, $k) => $v = sprintf( 137 "% ${colWidth}s", 138 ($k == 'size') ? filesize_h($v) : number_format($v) 139 ) 140 ); 141 142 echo $tr->format( 143 $widths, 144 array_merge([$ext], array_values($data)), 145 $colors 146 ); 147 } 148 } 149} 150