1: <?php
2:
3: try{
4: AlphaAutoLoader::loadLib('Find.php');
5: }catch (LibraryNotInstalledException $e) {
6: throw new LibraryNotInstalledException('Unable to generate code metrics as the PEAR::File_Find package is not installed!');
7: exit;
8: }
9:
10: /**
11: * Utility class for calcualting some software metics related to a project
12: *
13: * @package alpha::util::metrics
14: * @since 1.0
15: * @author John Collins <dev@alphaframework.org>
16: * @version $Id: AlphaMetrics.inc 1496 2012-02-12 20:32:21Z alphadev $
17: * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
18: * @copyright Copyright (c) 2012, John Collins (founder of Alpha Framework).
19: * All rights reserved.
20: *
21: * <pre>
22: * Redistribution and use in source and binary forms, with or
23: * without modification, are permitted provided that the
24: * following conditions are met:
25: *
26: * * Redistributions of source code must retain the above
27: * copyright notice, this list of conditions and the
28: * following disclaimer.
29: * * Redistributions in binary form must reproduce the above
30: * copyright notice, this list of conditions and the
31: * following disclaimer in the documentation and/or other
32: * materials provided with the distribution.
33: * * Neither the name of the Alpha Framework nor the names
34: * of its contributors may be used to endorse or promote
35: * products derived from this software without specific
36: * prior written permission.
37: *
38: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
39: * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
40: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
41: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
42: * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
43: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
45: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
48: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
49: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
50: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51: * </pre>
52: *
53: */
54: class AlphaMetrics {
55: /**
56: * The directory to begin the calculations from
57: *
58: * @var string
59: * @since 1.0
60: */
61: private $rootDir;
62:
63: /**
64: * The file extensions of the file types to include in the calculations
65: *
66: * @var array
67: * @since 1.0
68: */
69: private $includeFileTypes = array('.php', '.ini', '.html', '.phtml', '.inc', '.js', '.css', '.xml');
70:
71: /**
72: * Any sub-directories which you might want to exclude from the calculations
73: *
74: * @var array
75: * @since 1.0
76: */
77: private $excludeSubDirectories = array('cache','lib','docs','attachments','dist');
78:
79: /**
80: * The Total Lines of Code (TLOC) for the project
81: *
82: * @var integer
83: * @since 1.0
84: */
85: private $TLOC = 0;
86:
87: /**
88: * The Total Lines of Code (TLOC) for the project, less comments defined in $comments
89: *
90: * @var integer
91: * @since 1.0
92: */
93: private $TLOCLessComments = 0;
94:
95: /**
96: * The count of the source code files in the project
97: *
98: * @var integer
99: * @since 1.0
100: */
101: private $fileCount = 0;
102:
103: /**
104: * An array of fileName => lines of code to be populated by this class
105: *
106: * @var array
107: * @since 1.0
108: */
109: private $filesLOCResult = array();
110:
111: /**
112: * An array of fileName => lines of code to be populated by this class,
113: * excluding comment lines defined in the $comments array
114: *
115: * @var array
116: * @since 1.0
117: */
118: private $filesLOCNoCommentsResult = array();
119:
120: /**
121: * An array of the source code file names in the project
122: *
123: * @var array
124: * @since 1.0
125: */
126: private $files = array();
127:
128: /**
129: * An array of the directories in the project
130: *
131: * @var array
132: * @since 1.0
133: */
134: private $directories = array();
135:
136: /**
137: * An array of the first characters of a comment line in source code
138: *
139: * @var array
140: * @since 1.0
141: */
142: private $comments = array('/','*','#');
143:
144: /**
145: * Constructor, default $rootDir is .
146: *
147: * @param string $rootDir
148: * @since 1.0
149: */
150: public function __construct($rootDir = '.') {
151: $this->rootDir = $rootDir;
152: // populate the file and directories arrays using the File_Find class
153: list($this->directories, $this->files) = File_Find::maptree($rootDir);
154: }
155:
156: /**
157: * Calculates the Lines of Code (LOC)
158: *
159: * @since 1.0
160: */
161: public function calculateLOC() {
162: foreach ($this->files as $file) {
163: $file_type = substr($file, strrpos($file, '.'));
164: if (in_array($file_type, $this->includeFileTypes)) {
165: $exclude = false;
166: foreach ($this->excludeSubDirectories as $dir) {
167: if (preg_match("/".$dir."/i", $file)) {
168: $exclude = true;
169: }
170: }
171:
172: if (!$exclude) {
173: $current_file = file($file);
174:
175: $LOC = count($current_file);
176: $this->filesLOCResult[$file] = $LOC;
177: $LOC_less_comments = $this->disregardCommentsLOC($file);
178: $this->filesLOCNoCommentsResult[$file] = $LOC_less_comments;
179:
180: $this->TLOC += $LOC;
181: $this->TLOCLessComments += $LOC_less_comments;
182: $this->fileCount++;
183: }
184: }
185: }
186: }
187:
188: /**
189: * Generates a HTML table containing the metrics results
190: *
191: * @return string
192: * @since 1.0
193: */
194: public function resultsToHTML() {
195: $count = 1;
196:
197: $html = '<table class="list_view"><tr>';
198: $html .= '<th width="10%">File #:</th>';
199: $html .= '<th width="50%">File name:</th>';
200: $html .= '<th width="20%">Lines of Code (LOC):</th>';
201: $html .= '<th width="20%">Lines of Code (less comments):</th>';
202: $html .= '</tr>';
203: foreach(array_keys($this->filesLOCResult) as $result) {
204: $html .= "<tr><td>$count</td><td>$result</td><td>".$this->filesLOCResult[$result]."</td><td>".$this->filesLOCNoCommentsResult[$result]."</td></tr>";
205: $count++;
206: }
207: $html .= '</table>';
208:
209: $html .= "<p>Total files: ".number_format(count($this->files))."</p>";
210: $html .= "<p>Total source code files: ".number_format($this->fileCount)."</p>";
211: $html .= "<p>Total Lines of Code (TLOC): ".number_format($this->TLOC)."</p>";
212: $html .= "<p>Total Lines of Code (TLOC), less comments: ".number_format($this->TLOCLessComments)."</p>";
213:
214: return $html;
215: }
216:
217: /**
218: * Filters comments from LOC metric
219: *
220: * @param string $sourceFile
221: * @return integer
222: * @since 1.0
223: */
224: private function disregardCommentsLOC($sourceFile) {
225: $file = file($sourceFile);
226:
227: $LOC = 0;
228:
229: foreach ($file as $line) {
230: $exclude = false;
231: $line = ltrim($line);
232:
233: if(empty($line)) {
234: $exclude = true;
235: }else{
236: foreach ($this->comments as $comment) {
237: if (substr($line, 0, 1) == $comment) {
238: $exclude = true;
239: }
240: }
241: }
242:
243: if (!$exclude) {
244: $LOC++;
245: }
246: }
247:
248: return $LOC;
249: }
250: }
251:
252: ?>