//////////////////////////// common.php ////////////////////////////
= e($title) ?> | ICT3612 Assessment 3
Source code used for this task
isDir() ? rmdir($item->getPathname()) : unlink($item->getPathname());
}
rmdir($directory);
}
/**
* Extract ZIP entries individually after validating every relative path.
* This prevents an uploaded archive from writing outside the temporary folder.
*/
function safely_extract_text_files(ZipArchive $zip, $destination)
{
for ($index = 0; $index < $zip->numFiles; $index++) {
$entry = str_replace('\\', '/', $zip->getNameIndex($index));
if ($entry === '' || substr($entry, -1) === '/') {
continue;
}
if (
$entry[0] === '/' ||
preg_match('/^[A-Za-z]:/', $entry) ||
in_array('..', explode('/', $entry), true)
) {
throw new RuntimeException('The ZIP contains an unsafe file path.');
}
if (strtolower(pathinfo($entry, PATHINFO_EXTENSION)) !== 'txt') {
continue;
}
$target = $destination . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $entry);
$parent = dirname($target);
if (!is_dir($parent) && !mkdir($parent, 0700, true)) {
throw new RuntimeException('A temporary folder could not be created.');
}
$input = $zip->getStream($zip->getNameIndex($index));
$output = fopen($target, 'wb');
if (!$input || !$output) {
throw new RuntimeException('A text file in the ZIP could not be extracted.');
}
stream_copy_to_stream($input, $output);
fclose($input);
fclose($output);
}
}
function locate_classes_file($root)
{
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS)
);
foreach ($iterator as $file) {
if ($file->isFile() && strtolower($file->getFilename()) === 'classes.txt') {
return $file->getPathname();
}
}
throw new RuntimeException('The uploaded ZIP does not contain classes.txt.');
}
function analyse_dataset($zipPath)
{
if (!class_exists('ZipArchive')) {
throw new RuntimeException('The PHP Zip extension is not enabled on this server.');
}
$temporary = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'ict3612_' . bin2hex(random_bytes(8));
if (!mkdir($temporary, 0700, true)) {
throw new RuntimeException('The temporary analysis folder could not be created.');
}
try {
$zip = new ZipArchive();
if ($zip->open($zipPath) !== true) {
throw new RuntimeException('The uploaded file is not a valid ZIP archive.');
}
safely_extract_text_files($zip, $temporary);
$zip->close();
$classesPath = locate_classes_file($temporary);
$datasetRoot = dirname($classesPath);
$classes = array_values(array_unique(array_filter(
array_map('trim', file($classesPath, FILE_IGNORE_NEW_LINES)),
function ($value) {
return preg_match('/^-?\d+$/', $value);
}
)));
if (count($classes) < 2) {
throw new RuntimeException('classes.txt must contain at least two unique numbers.');
}
$counts = [];
$grandTotal = 0;
foreach (['train', 'validate', 'test'] as $folder) {
$folderPath = $datasetRoot . DIRECTORY_SEPARATOR . $folder;
if (!is_dir($folderPath)) {
throw new RuntimeException("The dataset is missing the {$folder} folder.");
}
$counts[$folder] = array_fill_keys($classes, 0);
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($folderPath, FilesystemIterator::SKIP_DOTS)
);
foreach ($files as $file) {
if (!$file->isFile() || strtolower($file->getExtension()) !== 'txt') {
continue;
}
foreach (file($file->getPathname(), FILE_IGNORE_NEW_LINES) as $line) {
if (preg_match('/^\s*(-?\d+)(?:\s|$)/', $line, $matches)) {
$class = $matches[1];
if (array_key_exists($class, $counts[$folder])) {
$counts[$folder][$class]++;
$grandTotal++;
}
}
}
}
}
return ['classes' => $classes, 'folders' => $counts, 'total' => $grandTotal];
} finally {
remove_directory($temporary);
}
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
try {
if (!isset($_FILES['dataset']) || $_FILES['dataset']['error'] !== UPLOAD_ERR_OK) {
throw new RuntimeException('Choose dataset.zip before starting the analysis.');
}
$upload = $_FILES['dataset'];
if ($upload['size'] > 10 * 1024 * 1024) {
throw new RuntimeException('The ZIP file must be no larger than 10 MB.');
}
if (strtolower(pathinfo($upload['name'], PATHINFO_EXTENSION)) !== 'zip') {
throw new RuntimeException('Only a .zip file can be analysed.');
}
$analysis = analyse_dataset($upload['tmp_name']);
} catch (Throwable $error) {
$errorMessage = $error->getMessage();
}
}
render_page_start('Task 3 · Dataset ZIP Analyser');
include 'menu.inc';
?>
Task 3
Count class instances in a dataset
Upload a ZIP containing classes.txt and the train, validate and test subfolders. Each line beginning with a class number is counted once.
= e($errorMessage) ?>
Analysis complete
Instance counts
$classCounts): ?>
= e(ucfirst($folder)) ?> total
= e(array_sum($classCounts)) ?>
Dataset total
= e($analysis['total']) ?>
| Folder | Class = e($class) ?> | Folder total |
$classCounts): ?>
| = e($folder) ?> |
= e($classCounts[$class]) ?> |
= e(array_sum($classCounts)) ?> |
| dataset total |
= e($classTotal) ?> |
= e($analysis['total']) ?> |