tkr/src/Framework/Filesystem/Filesystem.php
greg 9338332536 refactor-storage (#19)
Move ticks from filesystem into database.

I'm going to preserve the filesystem and just delete it manually. I'll add a login warning, but I'm pretty sure I'm the only person who'll ever be affected by this.

Co-authored-by: Greg Sarjeant <1686767+gsarjeant@users.noreply.github.com>
Reviewed-on: https://gitea.subcultureofone.org/greg/tkr/pulls/19
2025-07-24 02:12:31 +00:00

153 lines
5.4 KiB
PHP

<?php
// Validates that required directories exists
// and files have correct formats
class Filesystem {
public function validate(): void{
$this->validateStorageDir();
$this->validateStorageSubdirs();
$this->migrateTickFiles();
$this->moveTicksToDatabase();
}
// Make sure the storage/ directory exists and is writable
private function validateStorageDir(): void{
if (!is_dir(STORAGE_DIR)) {
throw new SetupException(
STORAGE_DIR . "does not exist. Please check your installation.",
'storage_missing'
);
}
if (!is_writable(STORAGE_DIR)) {
throw new SetupException(
STORAGE_DIR . "is not writable. Exiting.",
'storage_permissions'
);
}
}
// validate that the required storage subdirectories exist
// attempt to create them if they don't
private function validateStorageSubdirs(): void {
$storageSubdirs = array();
$storageSubdirs[] = CSS_UPLOAD_DIR;
$storageSubdirs[] = DATA_DIR;
$storageSubdirs[] = TICKS_DIR;
foreach($storageSubdirs as $storageSubdir){
if (!is_dir($storageSubdir)) {
if (!mkdir($storageSubdir, 0770, true)) {
throw new SetupException(
"Failed to create required directory: $dir",
'directory_creation'
);
}
}
if (!is_writable($storageSubdir)) {
if (!chmod($storageSubdir, 0770)) {
throw new SetupException(
"Required directory is not writable: $dir",
'directory_permissions'
);
}
}
}
}
// TODO: Delete this sometime before 1.0
// Add mood to tick files
private function migrateTickFiles(): void {
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(TICKS_DIR, RecursiveDirectoryIterator::SKIP_DOTS)
);
foreach ($files as $file) {
if ($file->isFile() && str_ends_with($file->getFilename(), '.txt')) {
$this->migrateTickFile($file->getPathname());
}
}
}
// TODO: Delete this sometime before 1.0
// Add mood field to tick files if necessary
private function migrateTickFile($filepath): void {
$lines = file($filepath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$modified = false;
// &$line creates a reference to the line in the array ($lines)
// so I can modify it in place
foreach ($lines as &$line) {
$fields = explode('|', $line);
if (count($fields) === 2) {
// Convert id|text to id|emoji|text
$line = $fields[0] . '||' . $fields[1];
$modified = true;
}
}
unset($line);
if ($modified) {
file_put_contents($filepath, implode("\n", $lines) . "\n");
// TODO: log properly
}
}
// TODO: Delete this sometime before 1.0
// Move ticks into database
private function moveTicksToDatabase(){
// It's a temporary migration function, so I'm not going to sweat the
// order of operations to let me reuse the global database.
$db = Database::get();
$count = $db->query("SELECT COUNT(*) FROM tick")->fetchColumn();
// Only migrate from filesystem if there are no ticks already in the database.
if ($count !== 0){
return;
}
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(TICKS_DIR, RecursiveDirectoryIterator::SKIP_DOTS)
);
foreach ($files as $file) {
if ($file->isFile() && str_ends_with($file->getFilename(), '.txt')) {
// Construct the date from the path and filename
$dir = pathinfo($file, PATHINFO_DIRNAME);
$dir_parts = explode('/', trim($dir, '/'));
[$year, $month] = array_slice($dir_parts, -2);
$day = pathinfo($file, PATHINFO_FILENAME);
$lines = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
// Get the time and the text, but discard the mood.
// I've decided against using it
$fields = explode('|', $line);
$time = $fields[0];
$tick = $fields[2];
$dateTime = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', "$year-$month-$day $time");
$tickDateTimeUTC = $dateTime->format('Y-m-d H:i:s');
$ticks[] = [$tickDateTimeUTC, $tick];
}
}
}
// Sort the ticks by dateTime
usort($ticks, function($a, $b) {
return strcmp($a[0], $b[0]);
});
// Save ticks to database
foreach ($ticks as $tick){
// Yes, silly, but I'm testing out the datetime/string SQLite conversion
$dateTime = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', "$tick[0]");
$timestamp = $dateTime->format('Y-m-d H:i:s');
$tickText = $tick[1];
$stmt = $db->prepare("INSERT INTO tick(timestamp, tick) values (?, ?)");
$stmt->execute([$timestamp, $tickText]);
}
}
}