@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
at upstream/main 137 lines 3.7 kB view raw
1<?php 2 3/** 4 * Local disk storage engine. Keeps files on local disk. This engine is easy 5 * to set up, but it doesn't work if you have multiple web frontends! 6 * 7 * @task internal Internals 8 */ 9final class PhabricatorLocalDiskFileStorageEngine 10 extends PhabricatorFileStorageEngine { 11 12 13/* -( Engine Metadata )---------------------------------------------------- */ 14 15 16 /** 17 * This engine identifies as "local-disk". 18 */ 19 public function getEngineIdentifier() { 20 return 'local-disk'; 21 } 22 23 public function getEnginePriority() { 24 return 5; 25 } 26 27 public function canWriteFiles() { 28 $path = PhabricatorEnv::getEnvConfig('storage.local-disk.path'); 29 $path = phutil_string_cast($path); 30 return (bool)strlen($path); 31 } 32 33 34/* -( Managing File Data )------------------------------------------------- */ 35 36 37 /** 38 * Write the file data to local disk. Returns the relative path as the 39 * file data handle. 40 * @task impl 41 */ 42 public function writeFile($data, array $params) { 43 $root = $this->getLocalDiskFileStorageRoot(); 44 45 // Generate a random, unique file path like "ab/29/1f918a9ac39201ff". We 46 // put a couple of subdirectories up front to avoid a situation where we 47 // have one directory with a zillion files in it, since this is generally 48 // bad news. 49 do { 50 $name = md5((string)mt_rand()); 51 $name = preg_replace('/^(..)(..)(.*)$/', '\\1/\\2/\\3', $name); 52 if (!Filesystem::pathExists($root.'/'.$name)) { 53 break; 54 } 55 } while (true); 56 57 $parent = $root.'/'.dirname($name); 58 if (!Filesystem::pathExists($parent)) { 59 execx('mkdir -p %s', $parent); 60 } 61 62 AphrontWriteGuard::willWrite(); 63 Filesystem::writeFile($root.'/'.$name, $data); 64 65 return $name; 66 } 67 68 69 /** 70 * Read the file data off local disk. 71 * @task impl 72 */ 73 public function readFile($handle) { 74 $path = $this->getLocalDiskFileStorageFullPath($handle); 75 return Filesystem::readFile($path); 76 } 77 78 79 /** 80 * Deletes the file from local disk, if it exists. 81 * @task impl 82 */ 83 public function deleteFile($handle) { 84 $path = $this->getLocalDiskFileStorageFullPath($handle); 85 if (Filesystem::pathExists($path)) { 86 AphrontWriteGuard::willWrite(); 87 Filesystem::remove($path); 88 } 89 } 90 91 92/* -( Internals )---------------------------------------------------------- */ 93 94 95 /** 96 * Get the configured local disk path for file storage. 97 * 98 * @return string Absolute path to somewhere that files can be stored. 99 * @task internal 100 */ 101 private function getLocalDiskFileStorageRoot() { 102 $root = PhabricatorEnv::getEnvConfig('storage.local-disk.path'); 103 104 if (!$root || $root == '/' || $root[0] != '/') { 105 throw new PhabricatorFileStorageConfigurationException( 106 pht( 107 "Malformed local disk storage root. You must provide an absolute ". 108 "path, and can not use '%s' as the root.", 109 '/')); 110 } 111 112 return rtrim($root, '/'); 113 } 114 115 116 /** 117 * Convert a handle into an absolute local disk path. 118 * 119 * @param string $handle File data handle. 120 * @return string Absolute path to the corresponding file. 121 * @task internal 122 */ 123 private function getLocalDiskFileStorageFullPath($handle) { 124 // Make sure there's no funny business going on here. Users normally have 125 // no ability to affect the content of handles, but double-check that 126 // we're only accessing local storage just in case. 127 if (!preg_match('@^[a-f0-9]{2}/[a-f0-9]{2}/[a-f0-9]{28}\z@', $handle)) { 128 throw new Exception( 129 pht( 130 "Local disk filesystem handle '%s' is malformed!", 131 $handle)); 132 } 133 $root = $this->getLocalDiskFileStorageRoot(); 134 return $root.'/'.$handle; 135 } 136 137}