1: <?php
2: namespace Contrib\Bundle\CoverallsV1Bundle\Collector;
3:
4: use Contrib\Bundle\CoverallsV1Bundle\Entity\JsonFile;
5: use Contrib\Bundle\CoverallsV1Bundle\Entity\SourceFile;
6:
7: /**
8: * Coverage collector for clover.xml.
9: *
10: * @author Kitamura Satoshi <with.no.parachute@gmail.com>
11: */
12: class CloverXmlCoverageCollector
13: {
14: /**
15: * JsonFile.
16: *
17: * @var \Contrib\Bundle\CoverallsV1Bundle\Entity\JsonFile
18: */
19: protected $jsonFile;
20:
21: // API
22:
23: /**
24: * Collect coverage from XML object.
25: *
26: * @param SimpleXMLElement $xml Clover XML object.
27: * @param string $rootDir Path to src directory.
28: * @return \Contrib\Bundle\CoverallsV1Bundle\Entity\JsonFile
29: */
30: public function collect(\SimpleXMLElement $xml, $rootDir)
31: {
32: $root = $rootDir . DIRECTORY_SEPARATOR;
33:
34: if (!isset($this->jsonFile)) {
35: $this->jsonFile = new JsonFile();
36: }
37:
38: // overwrite if run_at has already been set
39: $runAt = $this->collectRunAt($xml);
40: $this->jsonFile->setRunAt($runAt);
41:
42: $xpaths = array(
43: '/coverage/project/file',
44: '/coverage/project/package/file',
45: );
46:
47: foreach ($xpaths as $xpath) {
48: foreach ($xml->xpath($xpath) as $file) {
49: $srcFile = $this->collectFileCoverage($file, $root);
50:
51: if ($srcFile !== null) {
52: $this->jsonFile->addSourceFile($srcFile);
53: }
54: }
55: }
56:
57: return $this->jsonFile;
58: }
59:
60: // Internal method
61:
62: /**
63: * Collect timestamp when the job ran.
64: *
65: * @param SimpleXMLElement $xml Clover XML object of a file.
66: * @param string $format DateTime format.
67: * @return string
68: */
69: protected function collectRunAt(\SimpleXMLElement $xml, $format = 'Y-m-d H:i:s O')
70: {
71: $timestamp = $xml->project['timestamp'];
72: $runAt = new \DateTime('@' . $timestamp);
73:
74: return $runAt->format($format);
75: }
76:
77: /**
78: * Collect coverage data of a file.
79: *
80: * @param SimpleXMLElement $file Clover XML object of a file.
81: * @param string $root Path to src directory.
82: * @return NULL|\Contrib\Bundle\CoverallsV1Bundle\Entity\SourceFile
83: */
84: protected function collectFileCoverage(\SimpleXMLElement $file, $root)
85: {
86: $absolutePath = (string) $file['name'];
87:
88: if (false === strpos($absolutePath, $root)) {
89: return null;
90: }
91:
92: $filename = str_replace($root, '', $absolutePath);
93:
94: return $this->collectCoverage($file, $absolutePath, $filename);
95: }
96:
97: /**
98: * Collect coverage data.
99: *
100: * @param SimpleXMLElement $file Clover XML object of a file.
101: * @param string $path Path to source file.
102: * @param string $filename Filename.
103: * @return \Contrib\Bundle\CoverallsV1Bundle\Entity\SourceFile
104: */
105: protected function collectCoverage(\SimpleXMLElement $file, $path, $filename)
106: {
107: if ($this->jsonFile->hasSourceFile($path)) {
108: $srcFile = $this->jsonFile->getSourceFile($path);
109: } else {
110: $srcFile = new SourceFile($path, $filename);
111: }
112:
113: foreach ($file->line as $line) {
114: if ((string) $line['type'] === 'stmt') {
115: $lineNum = (int) $line['num'];
116:
117: if ($lineNum > 0) {
118: $srcFile->addCoverage($lineNum - 1, (int) $line['count']);
119: }
120: }
121: }
122:
123: return $srcFile;
124: }
125:
126: // accessor
127:
128: /**
129: * Return json file.
130: *
131: * @return \Contrib\Bundle\CoverallsV1Bundle\Entity\JsonFile
132: */
133: public function getJsonFile()
134: {
135: return $this->jsonFile;
136: }
137: }
138: