I have this function that is responsible for converting a file's name and mime-type into something more "human" (e.g. file.png, image/png to [Image, PNG]). What I found interesting was that groups of if() elseif()
statements had a higher NPath complexity than a switch(true)
statement.
With the following code, PHP Mess Detector outputs an NPath of 4410:
public function humanKind()
{
$typeRA = explode("/", strtolower($this->type));
$fileRA = explode(".", $this->name);
$fileType = strtoupper($fileRA[count($fileRA) - 1]);
switch($typeRA[0]) {
case "image":
$humanType = "Image";
break;
case "video":
$humanType = "Video";
break;
case "audio":
$humanType = "Sound";
break;
case "font":
$humanType = "Font";
break;
default:
$humanType = "File";
}
switch ($this->type) {
case "application/msword":
case "application/pdf":
case "applicaiton/wordperfect":
case "text/plain":
case "text/rtf":
case "image/vnd.photoshop":
case "image/psd":
case "image/vnd.adobe.photoshop":
case "image/x-photoshop":
case "application/xml":
case "application/x-mspublisher":
case "text/html":
case "application/xhtml+xml":
case "text/richtext":
case "application/rtf":
case "application/x-iwork-pages-sffpages":
case "application/vnd.apple.pages":
$humanType = "Document";
break;
case "application/vnd.ms-excel":
case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
case "application/x-iwork-numbers-sffnumbers":
case "application/vnd.apple.numbers":
$humanType = "Spreadsheet";
break;
case "application/vnd.ms-powerpoint":
case "application/vnd.openxmlformats-officedocument.presentationml.presentation":
case "application/vnd.openxmlformats-officedocument.presentationml.slideshow":
case "application/x-iwork-keynote-sffkey":
case "application/vnd.apple.keynote":
$humanType = "Slideshow";
break;
case "application/zip":
case "application/x-zip-compressed":
case "application/x-compressed":
case "application/x-compress":
case "application/x-rar-compressed":
case "applicaiton/x-7z-compressed":
case "application/x-ace-compressed":
$humanType = "Archive";
break;
case "text/x-vcard":
case "text/x-ms-contact":
$humanType = "Contact";
break;
case "text/x-php":
case "application/x-dosexec":
case "application/x-xpinstall":
case "application/x-opera-extension":
case "application/x-chrome-extension":
case "application/x-perl":
case "application/x-shockwave-flash":
case "application/java-archive":
$humanType = "Program";
break;
case "application/vnd.ms-fontobject":
case "application/font-woff":
case "application/x-font-truetype":
case "application/x-font-opentype":
case "application/x-font-ttf":
case "application/font-sfnt":
$humanType = "Font";
break;
}
// Special Cases
if ($humanType == "Archive" && $fileType == "APK") { // Android App
$humanType = "App";
} elseif ($humanType == "Archive" && $fileType == "XPS") {
$humanType = "Document";
} elseif ($this->type == "application/xml" && $fileType == "CONTACT") {
$humanType = "Contact";
} elseif ($this->type == "application/octet-stream" && $fileType == "JNT") {
$humanType = "Document";
}
if (strlen($fileType) > 4) {
$fileType = "";
}
return array($humanType, $fileType);
If we then replace the special cases if elseif
with the below:
// Special Cases
switch(true) {
case ($humanType == "Archive" && $fileType == "APK"): // Android App
$humanType = "App";
break;
case ($humanType == "Archive" && $fileType == "XPS"):
$humanType = "Document";
break;
case ($this->type == "application/xml" && $fileType == "CONTACT"):
$humanType = "Contact";
break;
case ($this->type == "application/octet-stream" && $fileType == "JNT"):
$humanType = "Document";
break;
}
PHP Mess Detector reports an NPath Complexity of 1960.
Why is this? What makes switch(true) less complex than what appears to me to be just about the same control structure?
Since NPath complexity measures the number of unit tests required to get complete coverage of your code there should be no difference between your 2 "Special Cases" implementations.
But there is some difference in the calculation. Let's step through the 2 "Special Cases" implementations and calculate the NPath Complexity manually:
if .. elseif ..
if ($humanType == "Archive" && $fileType == "APK") { // Android App
$humanType = "App";
}
elseif ($humanType == "Archive" && $fileType == "XPS") {
$humanType = "Document";
}
elseif ($this->type == "application/xml" && $fileType == "CONTACT") {
$humanType = "Contact";
}
elseif ($this->type == "application/octet-stream" && $fileType == "JNT") {
$humanType = "Document";
}
This statement results in NPath Complexity of 9: 1 point for if .. else
, 1 point for every if(expr)
and 1 point for every &&
operator. (1 + 4 + 4 = 9)
switch(true)
switch(true) {
case ($humanType == "Archive" && $fileType == "APK"): // Android App
$humanType = "App";
break;
case ($humanType == "Archive" && $fileType == "XPS"):
$humanType = "Document";
break;
case ($this->type == "application/xml" && $fileType == "CONTACT"):
$humanType = "Contact";
break;
case ($this->type == "application/octet-stream" && $fileType == "JNT"):
$humanType = "Document";
break;
}
And this statement results in NPath Complexity of only 4: 0 points for switch(true)
because it contains no &&
or ||
operators and 1 point for every case
label. (0 + 4 = 4)
humanKind
functionNPath values are calculated for each statement and than the values are multiplied. The NPath Complexity of your function without the "Special Cases" statement is 490. Multiplied with the NPath value for the if .. else if ..
statement of 9 you get a NPath Complexity of 4410. And multiplied with the NPath value for the switch(true)
statement of 4 you get a complexity of only 1960. That's all!
And now we know: NPath Complexity does not measure expression complexity of case
labels in switch
statements!