LineCounter.class.php

Aus Programmers Guide

Wechseln zu: Navigation, Suche

LineCounter.class.php

<?php
//to get McCabes cyclomatic complexity of the Source-Code
//http://code.google.com/p/phpcyclo/source/browse/trunk/+phpcyclo+--username+bolter.fire/lint_php_lib.php?spec=svn2&r=2
//http://github.com/pceres
define(MCCABE,true);
 
if(MCCABE){
	require_once('lint_php_lib.php');
}
/* CLASS LineCounter
 *
 * This class counts the lines of code (LOC) in an existing Project-Folder.
 * 
 * #############################
 * USAGE:
 *
 * $count = new LineCounter();
 * $count->setFolder('/var/www/search/');
 * $count->setFileTypes('php,txt');
 * OR
 * $count = new LineCounter('/var/www/','php,txt');
 *
 * $count->scanDirectory();
 * echo $count->getLines()." total Lines<br>";
 * echo $count->getSize()." byte<br>";
 * echo $count->getFiles()." Files<br>";
 * echo $count->getCommentLines()." CommentLine<br>";
 * echo $count->getEmptyLines()." EmtyLine<br>";
 * $count->displayExactOutput();
 *
 * ############################
 * METHODS:
 * __construct([string $folder, string $fileTypes])
 *
 * public void setFolder(string $folder)
 * public void setFileTypes(string $fileTypes)
 * public void setMcCabe(boolean $mcCabe)
 * public void scanDirectory(void)
 *
 * public int getLines(void)
 * public int getSize(void)
 * public int getFiles(void)
 * public int getCommentLines(void)
 * public int getEmptyLines(void)
 *
 * public void displayExactOutput(void)
 *
 * void countLines
 * void lineScan
 * void throwNotScannedException
 *
 *
 *
 *
 * @author Roy Bohn <roybohn11@web.de>  ->  www.roy-bohn.de
 * @date 2010-05-10
 */
 
 
class LineCounter{
 
    // declare class variables
	private $fileName;
	private $folder;
	private $file;
	private $fileType;
	private $fileHandle;
	private $dirIterator;
	private $line;
	private $val;
	private $value;
	private $actVal;
	private $percentComments;
	private $cc;
	private $ccWert;
	private $ccFunction;
	private $filePointer;
 
	private $fileTypeArray  = array();
	private $latestValues = array();
	private $result = array();
 
	/**
	 * Constructor - Is called when the class is instanced
	 *
	 * @access: public
	 * @param Str $folder
	 * @param Str $fileTypes
	 *
	 * @return NONE
	 */
	public function __construct($folder='.',$fileTypes='php'){
		//Deklarieren und Initialisieren der Variablen
		$this->lines = 0;				//globaler Zeilenzaehler
		$this->commentLines = 0;		//globaler Kommentarzaehler
		$this->emptyLines = 0;			//globaler Leerzeilenzaehler
		$this->outputArray = array();	//fuer die Statistikausgabe
		$this->fileTypes = explode(",",$fileTypes);	//inkludierte Dateitypen
		$this->files = 0;				//globaler Dateienzaehler
		$this->size = 0;				//globaler Dateigroessenzaehler
		$this->folder = $folder;		//Der Pfad
		$this->isScanned = false;		//fuer die Fehlerbehandlung scan-Indikator
		$this->CC = array();			//Array fuer McCabes cyclomatic complexity
 
		if(MCCABE){
			$this->mcCabe = true;
		}else{
			$this->mcCabe = false;
		}
	}
 
	/**
	 * scanDirectory() Scanning the given Directory recursive and filter the FileTypes
	 *
	 * @access: public
	 *
	 * @return NONE
	 */
	public function scanDirectory(){
 
		//DirIterator setzen
		$dirIterator = new RecursiveDirectoryIterator($this->folder);
 
		//Ueber das DIR Iterieren
		foreach (new RecursiveIteratorIterator($dirIterator) as $fileName => $file) {
 
			//fuer den Regex das Array mit | zusammenfuehern
			$fileTypes = implode("|",$this->fileTypes);
 
			//per Regex die Dateien filtern
			if(preg_match("/^.+\.(" . $fileTypes . ")$/i",$fileName)){
				$this->size += $file->getSize();
				$this->countLines(basename($fileName), $file);
				if($this->mcCabe)
					$this->determineCC($fileName);
				$this->files++;
			}
		}
 
		//die Variable dient nur der Fehlerbehandlung weiter unten
		$this->isScanned=true;
	}//END:scanDirectory()
 
	/**
	 * countLines() Counts the occurent Lines for each file
	 *
	 * @access: private
	 * @param Str $fileName
	 * @param Str $file
	 *
	 * @return NONE
	 */
	private function countLines($fileName,$file){
		//Letzten Stand festhalten, damit wir die aktiven Werte clever bestimmen koennen
		$latestValues = array($this->commentLines,$this->emptyLines,$this->lines);
 
		//Datei lesen
		if ($fileHandle = fopen($file, 'r')) {
			while (!feof($fileHandle)) {
				if ($line = fgets($fileHandle)) {
					$this->lineScan($line);
				}
			}
			fclose($fileHandle);
		}
 
		//Das Array fuer die Detailierte Ausgabe fuellen 
		array_push($this->outputArray, array($fileName,$this->commentLines-$latestValues[0],$this->emptyLines-$latestValues[1],$this->lines-$latestValues[2]));
	}//END:countLines()
 
	/**
	 * lineScan() categorize the given Line
	 *
	 * @access: private
	 * @param Str $line
	 *
	 * @return NONE
	 */
	private function lineScan($line){
 
		//der Regex passt auf Zeilen die folgendermaßen beginnen:
		// '/*'  '*'  '*/'  '//'
		if(preg_match('/^\/\/|^\/\*|^\*|^\*\//',trim($line))){
			$this->commentLines++;
		}
 
		//wenn der Regex keine geschriebene Zeile erkennt deklarieren wir als leer
		if(!preg_match('/./',trim($line))){
			$this->emptyLines++;
		}
		//Zeilen hochzaehlen
		$this->lines++;
	}//END:lineScan();
 
 
	/**
	 * determineCC() determine the cyclomatic complexity of the given file
	 *
	 * @access: private
	 * @param Str $file
	 *
	 * @return NONE
	 */
	private function determineCC($file){
 
		//Wir oeffenen ein zweites mal die Datei, koennte optimiert werden
		$filePointer = file_get_contents($file);
 
		//wenn die Datei leer ist muehen wir uns nicht weiter
		if($filePointer != ''){
			$result = lint($filePointer,0);
			foreach ($result[0]['lista_functions'] as $value){
				if($value['function'] != ""){
					$this->CC [basename($file)] [$value['function']] = $value['mc_count'];
				}
			}
		}
	}
 
 
 
 
	/**
	 * throwNotScannedException() Throw an Exception 
	 *
	 * @access: private
	 * @throws Exception
	 * @return NONE
	 */
	private function throwNotScannedException(){
		throw new Exception("Es wurde noch nicht gescannt bitte: LineCounter::scanDirectory() aufrufen!");
	}
 
	/*	###	###	###	###	###	###	Getters and Setters	###	###	###	###	##*/
	/**
	 * getLines() get the whole Lines
	 *
	 * @access: public
	 *
	 * @return Int $lineCounter
	 */
	public function getLines(){
		if($this->isScanned)
			return $this->lines;
		else
			$this->throwNotScannedException();
	}
 
	/**
	 * getFiles() get the whole Files
	 *
	 * @access: public
	 *
	 * @return Int $fileCounter
	 */
	public function getFiles(){
		if($this->isScanned)
			return $this->files;
		else
			$this->throwNotScannedException();
	}
 
	/**
	 * getSize() get the calculated filesize
	 *
	 * @access: public
	 *
	 * @return Int $fileSize
	 */
	public function getSize(){
		if($this->isScanned)
			return $this->size;	
		else
			$this->throwNotScannedException();
	}
 
	/**
	 * getCommentLines() get the whole Commentlines 
	 *
	 * @access: public
	 *
	 * @return Int $commentedLines
	 */
	public function getCommentLines(){
		if($this->isScanned)
			return $this->commentLines;
		else
			$this->throwNotScannedException();
	}
 
	/**
	 * getEmptyLines() get the whole empty Lines 
	 *
	 * @access: public
	 *
	 * @return Int $emptyLines
	 */
	public function getEmptyLines(){
		if($this->isScanned)
			return $this->emptyLines;
		else
			$this->throwNotScannedException();
	}
 
	/**
	 * getExactOutput() get an array including statistics
	 *
	 * @access: public
	 *
	 * @return Array $statisticArray
	 */
	public function getExactOutput(){
		if($this->isScanned)
			return $this->outputArray;
		else
			$this->throwNotScannedException();
	}
 
	/**
	 * displayExactOutput() show statistics
	 *
	 * @access: public
	 * @throws Exception
	 *
	 * @return NONE
	 */
	public function displayExactOutput(){
		if($this->isScanned){
			echo "<table border=\"1\">"; 
			echo "<tr><th>Dateiname</th><th>Kommentare</th><th>Leerzeilen</th><th>Gesamtzeilen</th><th>Lines of Code (LOC)</th><th>% Kommentare</th>";
			if($this->mcCabe)
				echo "<th>McCabes cc >10</th>";
			echo "</tr>";
			foreach($this->getExactOutput() as $val){
				//Gesamtzeilen - Kommentarzeilen - Leerzeilen
				$LOC = $val[3] - $val[2] - $val[1];
				if($val[3]!=0){//wir duefen ja nicht durch 0 dividieren
					$percentComments=round($val[1]/$val[3]*100);
				}
				//Man sagt, dass 30% Kommentare in den Quelltext gehoeren
				if($percentComments <= 30){
					echo "<tr style=\"background-color:#FFDDDD\">";
				}else{
					echo "<tr style=\"background-color:lightgreen\">";
				}
 
				foreach($val as $actVal){
					echo "<td>$actVal</td>";
				}
 
				echo "<td style=\"color:red;font-weight:bold;\">".($LOC)."</td>";
				echo "<td>$percentComments</td>";
				if($this->mcCabe){
					echo "<td>";
					$cc = $this->CC [$val[0]];
					if(isset($cc)){
						foreach($cc as $ccFunction=>$ccWert){
							if($ccWert>=10){
								echo $ccFunction." : <b>".$ccWert."</b><br>";
							}else{
								//echo $ccFunction." : ".$ccWert."<br>";
							}
						}
					}else{
						echo "null";
					}
					echo "</td>";
				}
				echo "</tr>";
			}
			echo "</table>";
		}else
			$this->throwNotScannedException();
	}//END:displayExactOutput();
 
	/**
	 * getCC() get the cyclomatic complexity
	 *
	 * @access: public
	 *
	 * @return Arr $cyclomaticComplexity
	 */
 
	public function getCC(){
		return $this->CC;
	}
 
 
	/**
	 * setFolder() set the folder to scan
	 *
	 * @access: public
	 * @param Str $folder
	 *
	 * @return NONE
	 */
	public function setFolder($folder){
		//Fehlerbehandlung
		if($folder == '' || !isset($folder)){
			throw new Exception("Keinen Pfad uebergeben!");
		}elseif(!is_dir($folder)){
			throw new Exception("'$folder' ist kein Verzeichnis!");
		}elseif(!is_readable($folder)){
			throw new Exception("Ich kann '$folder' nicht lesen, bitte ueberpruefen Sie die Rechte!");
		}else{
			//alles okay
			$this->folder = $folder;
		}
	}//END:setFolder
 
	/**
	 * setFileTypes() set the filetypes to include
	 *
	 * @access: public
	 * @throws Exception
	 * @param Str $fileType
	 *
	 * @return NONE
	 */
	public function setFileTypes($fileType){
		//Fehlerbehandlung
		if($fileType == '' || !isset($fileType)){
			throw new Exception("Nichts uebergeben");
		}elseif(preg_match('/,/',$fileType)){
			//es ist eine Komma separierte Liste
			$fileTypeArray = explode(",",$fileType);
			$this->fileTypes = $fileTypeArray;
		}else{
			$this->fileTypes = array($fileType);
		}
	}
 
	/**
	 * setMcCabe() to turn McCabe off - this saves time
	 *
	 * @access: public
	 * @throws Exception
	 * @param bool $mcCabe
	 *
	 * @return NONE
	 */
	public function setMcCabe($mcCabe){
		if(!MCCABE){
			throw new Exception("Bitte MCCABE in der Datei LineCounter.class.php auf true setzen!");
		}
		if(is_bool($mcCabe)){
			$this->mcCabe=$mcCabe;
		}else{
			throw new Exception("Bitte nur boolsche Werte uebergeben");
		}
	}
 
}//end LineCounter class
?>
Persönliche Werkzeuge