/vendor/ua-parser/uap-php/src/Command/LogfileCommand.php

https://gitlab.com/fabiorf/chat · PHP · 205 lines · 167 code · 30 blank · 8 comment · 12 complexity · 0887753bd217ca928bf4373514580e68 MD5 · raw file

  1. <?php
  2. /**
  3. * ua-parser
  4. *
  5. * Copyright (c) 2011-2012 Dave Olsen, http://dmolsen.com
  6. *
  7. * Released under the MIT license
  8. */
  9. namespace UAParser\Command;
  10. use Symfony\Component\Console\Command\Command;
  11. use Symfony\Component\Console\Input\InputArgument;
  12. use Symfony\Component\Console\Input\InputInterface;
  13. use Symfony\Component\Console\Input\InputOption;
  14. use Symfony\Component\Console\Output\OutputInterface;
  15. use Symfony\Component\Filesystem\Filesystem;
  16. use Symfony\Component\Finder\Finder;
  17. use Symfony\Component\Finder\SplFileInfo;
  18. use UAParser\Exception\InvalidArgumentException;
  19. use UAParser\Exception\ReaderException;
  20. use UAParser\Parser;
  21. use UAParser\Result\Client;
  22. use UAParser\Util\Logfile\AbstractReader;
  23. class LogfileCommand extends Command
  24. {
  25. protected function configure()
  26. {
  27. $this
  28. ->setName('ua-parser:log')
  29. ->setDescription('Parses the supplied webserver log file.')
  30. ->addArgument(
  31. 'output',
  32. InputArgument::REQUIRED,
  33. 'Path to output log file'
  34. )
  35. ->addOption(
  36. 'log-file',
  37. 'f',
  38. InputOption::VALUE_REQUIRED,
  39. 'Path to a webserver log file'
  40. )
  41. ->addOption(
  42. 'log-dir',
  43. 'd',
  44. InputOption::VALUE_REQUIRED,
  45. 'Path to webserver log directory'
  46. )
  47. ->addOption(
  48. 'include',
  49. 'i',
  50. InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED,
  51. 'Include glob expressions for log files in the log directory',
  52. array('*.log', '*.log*.gz', '*.log*.bz2')
  53. )
  54. ->addOption(
  55. 'exclude',
  56. 'e',
  57. InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED,
  58. 'Exclude glob expressions for log files in the log directory',
  59. array('*error*')
  60. )
  61. ;
  62. }
  63. protected function execute(InputInterface $input, OutputInterface $output)
  64. {
  65. if (!$input->getOption('log-file') && !$input->getOption('log-dir')) {
  66. throw InvalidArgumentException::oneOfCommandArguments('log-file', 'log-dir');
  67. }
  68. $parser = Parser::create();
  69. $undefinedClients = array();
  70. /** @var $file SplFileInfo */
  71. foreach ($this->getFiles($input) as $file) {
  72. $path = $this->getPath($file);
  73. $lines = file($path);
  74. if (empty($lines)) {
  75. $output->writeln(sprintf('Skipping empty file "%s"', $file->getPathname()));
  76. $output->writeln('');
  77. continue;
  78. }
  79. $firstLine = reset($lines);
  80. $reader = AbstractReader::factory($firstLine);
  81. if (!$reader) {
  82. $output->writeln(sprintf('Could not find reader for file "%s"', $file->getPathname()));
  83. $output->writeln('');
  84. continue;
  85. }
  86. $output->writeln('');
  87. $output->writeln(sprintf('Analyzing "%s"', $file->getPathname()));
  88. $count = 1;
  89. $totalCount = count($lines);
  90. foreach ($lines as $line) {
  91. try {
  92. $userAgentString = $reader->read($line);
  93. } catch (ReaderException $e) {
  94. $count = $this->outputProgress($output, 'E', $count, $totalCount);
  95. continue;
  96. }
  97. $client = $parser->parse($userAgentString);
  98. $result = $this->getResult($client);
  99. if ($result !== '.') {
  100. $undefinedClients[] = json_encode(
  101. array($client->toString(), $userAgentString),
  102. JSON_UNESCAPED_SLASHES
  103. );
  104. }
  105. $count = $this->outputProgress($output, $result, $count, $totalCount);
  106. }
  107. $this->outputProgress($output, '', $count - 1, $totalCount, true);
  108. $output->writeln('');
  109. }
  110. $undefinedClients = $this->filter($undefinedClients);
  111. $fs = new Filesystem();
  112. $fs->dumpFile($input->getArgument('output'), join(PHP_EOL, $undefinedClients));
  113. }
  114. private function outputProgress(OutputInterface $output, $result, $count, $totalCount, $end = false)
  115. {
  116. if (($count % 70) === 0 || $end) {
  117. $formatString = '%s %' . strlen($totalCount) . 'd / %-' . strlen($totalCount) . 'd (%3d%%)';
  118. $result = $end ? str_repeat(' ', 70 - ($count % 70)) : $result;
  119. $output->writeln(sprintf($formatString, $result, $count, $totalCount, $count / $totalCount * 100));
  120. } else {
  121. $output->write($result);
  122. }
  123. return $count + 1;
  124. }
  125. private function getResult(Client $client)
  126. {
  127. if ($client->device->family === 'Spider') {
  128. return '.';
  129. } elseif ($client->ua->family === 'Other') {
  130. return 'U';
  131. } elseif ($client->os->family === 'Other') {
  132. return 'O';
  133. } elseif ($client->device->family === 'Generic Smartphone') {
  134. return 'S';
  135. } elseif ($client->device->family === 'Generic Feature Phone') {
  136. return 'F';
  137. }
  138. return '.';
  139. }
  140. private function getFiles(InputInterface $input)
  141. {
  142. $finder = Finder::create();
  143. if ($input->getOption('log-file')) {
  144. $file = $input->getOption('log-file');
  145. $finder->append(Finder::create()->in(dirname($file))->name(basename($file)));
  146. }
  147. if ($input->getOption('log-dir')) {
  148. $dirFinder = Finder::create()
  149. ->in($input->getOption('log-dir'));
  150. array_map(array($dirFinder, 'name'), $input->getOption('include'));
  151. array_map(array($dirFinder, 'notName'), $input->getOption('exclude'));
  152. $finder->append($dirFinder);
  153. }
  154. return $finder;
  155. }
  156. private function filter(array $lines)
  157. {
  158. return array_values(array_unique($lines));
  159. }
  160. private function getPath(SplFileInfo $file)
  161. {
  162. switch ($file->getExtension()) {
  163. case 'gz':
  164. $path = 'compress.zlib://' . $file->getPathname();
  165. break;
  166. case 'bz2':
  167. $path = 'compress.bzip2://' . $file->getPathname();
  168. break;
  169. default:
  170. $path = $file->getPathname();
  171. break;
  172. }
  173. return $path;
  174. }
  175. }