180 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			PHP
		
	
	
	
			
		
		
	
	
			180 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			PHP
		
	
	
	
<?php
 | 
						|
 | 
						|
class Item implements JsonSerializable
 | 
						|
{
 | 
						|
 | 
						|
    // TODO: create a persist() method that updates the line if the offset where
 | 
						|
    //       it originally existed in is given by 'ftell'. If it doesn't have an
 | 
						|
    //       ID, then try to find it in the file and if it doesn't exist, append
 | 
						|
    //
 | 
						|
    // TODO: Maybe implement tags as an associative array or somethign?
 | 
						|
    //
 | 
						|
    // TODO: Change to id here in all occurrences
 | 
						|
    //
 | 
						|
 | 
						|
    private string $hash;
 | 
						|
    private string $extension;
 | 
						|
    private array $tags = [];
 | 
						|
 | 
						|
    public static function load(MediaDB $db, string $id): ?Item
 | 
						|
    {
 | 
						|
        $ret = null;
 | 
						|
 | 
						|
        $db->map(function ($line) use (&$ret, $id) {
 | 
						|
            if ($line[0] == $id) {
 | 
						|
                $ret = Item::fromLine($line);
 | 
						|
                return true;
 | 
						|
            }
 | 
						|
 | 
						|
            return false;
 | 
						|
        });
 | 
						|
 | 
						|
        return $ret;
 | 
						|
    }
 | 
						|
 | 
						|
    public static function fromLine(array $line): Item
 | 
						|
    {
 | 
						|
        $ret = new Item();
 | 
						|
 | 
						|
        $ret->hash = array_shift($line);
 | 
						|
        $ret->tags = $line;
 | 
						|
        $ret->extensionFromTags();
 | 
						|
 | 
						|
        return $ret;
 | 
						|
    }
 | 
						|
 | 
						|
    // ::upload() requires the PHP file upload ARRAY from $_FILES
 | 
						|
    public static function upload(array $php_file, string $tags): Item
 | 
						|
    {
 | 
						|
        $from_path = $php_file['tmp_name'] ?? null;
 | 
						|
 | 
						|
        // --- CHECKS ---
 | 
						|
        if (!is_string($from_path))
 | 
						|
            Json::error('Passed invalid upload structure')->die();
 | 
						|
        if (!is_uploaded_file($from_path))
 | 
						|
            Json::error('Trying to upload illegal or non-existent file')->die();
 | 
						|
 | 
						|
        // --- INITIALIZE ---
 | 
						|
        $item = new Item();
 | 
						|
        $item->hash = hash_file('sha256', $from_path);
 | 
						|
        $item->tags = explode(' ', $tags);
 | 
						|
        $item->extensionFromUpload($php_file);
 | 
						|
 | 
						|
        // --- ACTUALLY GRAB FILE ---
 | 
						|
        $new_path = $item->getPath();
 | 
						|
        if (file_exists($new_path))
 | 
						|
            Json::error('File already exists')->die();
 | 
						|
        if (!move_uploaded_file($from_path, $new_path))
 | 
						|
            Json::error('Failed to move uploaded file')->die();
 | 
						|
 | 
						|
        MediaDB::add($item);
 | 
						|
 | 
						|
        return $item;
 | 
						|
    }
 | 
						|
 | 
						|
    private function extensionFromTags()
 | 
						|
    {
 | 
						|
        foreach ($this->tags as $i_tag) {
 | 
						|
            $parts = explode(':', $i_tag);
 | 
						|
            if ($parts[0] == 'format') {
 | 
						|
                $this->extension = $parts[1];
 | 
						|
                return;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        Json::error('Detected file without file format tag')->die();
 | 
						|
    }
 | 
						|
 | 
						|
    // Assumes is_uploaded_file has been done
 | 
						|
    private function extensionFromUpload(array $php_file)
 | 
						|
    {
 | 
						|
        $path = $php_file['tmp_name'] ?? false;
 | 
						|
        $name = $php_file['name'] ?? null;
 | 
						|
 | 
						|
        // Try to get extension from mimetype
 | 
						|
        $ext = (new finfo(FILEINFO_EXTENSION))->file($path);
 | 
						|
 | 
						|
        // REVIEW: Maybe change it so it doesn't have to be separated like this but
 | 
						|
        //         do somethign like ['mime/type', null] be equivalent to this?
 | 
						|
        if (array_search($ext, ['webp', 'png', 'jpeg']) !== false) {
 | 
						|
            goto success;
 | 
						|
        }
 | 
						|
 | 
						|
        // If it doesn't work, try to figure it out from the original extension
 | 
						|
        if ($ext == '???') {
 | 
						|
            // We mustn't accept everything straight away, so we're only accepting
 | 
						|
            // mime-type and original extension combinations that make sense
 | 
						|
            $whitelist = [
 | 
						|
                ['application/zip', 'kra'],
 | 
						|
                ['application/zip', 'krz'],
 | 
						|
            ];
 | 
						|
 | 
						|
            // NOTE: The original extension has to be grabbed from the original name
 | 
						|
            $mime = mime_content_type($path);
 | 
						|
            $ext  = pathinfo($name, PATHINFO_EXTENSION);
 | 
						|
 | 
						|
            if (array_search([$mime, $ext], $whitelist) !== false) {
 | 
						|
                goto success;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        Json::error('File mime-type and or extension not allowed.')->die();
 | 
						|
 | 
						|
        success:
 | 
						|
        $this->extension = $ext;
 | 
						|
        $this->tags[] = 'format:' . $ext;
 | 
						|
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    public function getHash(): string
 | 
						|
    {
 | 
						|
        return $this->hash;
 | 
						|
    }
 | 
						|
 | 
						|
    public function getExtension(): string
 | 
						|
    {
 | 
						|
        return $this->extension;
 | 
						|
    }
 | 
						|
 | 
						|
    public function getTags(): array
 | 
						|
    {
 | 
						|
        return $this->tags;
 | 
						|
    }
 | 
						|
 | 
						|
    public function getPath(bool $absolute = false): string
 | 
						|
    {
 | 
						|
        return sprintf(
 | 
						|
            '%s/%s.%s',
 | 
						|
            $GLOBALS[$absolute ? 'path_media' : 'path_media'],
 | 
						|
            $this->hash,
 | 
						|
            $this->extension
 | 
						|
        );
 | 
						|
    }
 | 
						|
    public function getUri(): string
 | 
						|
    {
 | 
						|
        // TODO: Implement relative
 | 
						|
        return sprintf(
 | 
						|
            '%s/%s/%s.%s',
 | 
						|
            $GLOBALS['base_root'],
 | 
						|
            $GLOBALS['base_media'],
 | 
						|
            $this->hash,
 | 
						|
            $this->extension,
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    public function jsonSerialize(): array
 | 
						|
    {
 | 
						|
        return [
 | 
						|
            'id'   => $this->hash,
 | 
						|
            'link' => $this->getUri(),
 | 
						|
            'tags' => $this->tags,
 | 
						|
        ];
 | 
						|
    }
 | 
						|
 | 
						|
    public function getJson(): Json
 | 
						|
    {
 | 
						|
        return Json::new($this);
 | 
						|
    }
 | 
						|
}
 |