//////////////////////////// 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.

Upload dataset.zip

Analysis complete

Instance counts

$classCounts): ?>
total
Dataset total
$classCounts): ?>
FolderClass Folder total
dataset total