103 public static function recursiveCopy(
string $origin,
string $destination) : void{
104 if(!is_dir($origin)){
105 throw new \RuntimeException(
"$origin does not exist, or is not a directory");
107 if(!is_dir($destination)){
108 if(file_exists($destination)){
109 throw new \RuntimeException(
"$destination already exists, and is not a directory");
111 if(!is_dir(dirname($destination))){
113 throw new \RuntimeException(
"The parent directory of $destination does not exist, or is not a directory");
116 ErrorToExceptionHandler::trap(fn() => mkdir($destination));
117 }
catch(\ErrorException $e){
118 if(!is_dir($destination)){
119 throw new \RuntimeException(
"Failed to create output directory $destination: " . $e->getMessage());
123 self::recursiveCopyInternal($origin, $destination);
139 self::recursiveCopyInternal(Path::join($origin, $object), Path::join($destination, $object));
171 $result = ltrim(str_replace($cleanPath, $replacement, $result),
"/");
188 }
catch(\ErrorException $e){
189 throw new \InvalidArgumentException(
"Failed to open lock file: " . $e->getMessage(), 0, $e);
191 if(!flock($resource, LOCK_EX | LOCK_NB)){
194 flock($resource, LOCK_SH);
195 $pid = Utils::assumeNotFalse(stream_get_contents($resource),
"This is a known valid file resource, at worst we should receive an empty string");
196 if(preg_match(
'/^\d+$/', $pid) === 1){
201 ftruncate($resource, 0);
202 fwrite($resource, (
string) getmypid());
204 flock($resource, LOCK_SH);
205 self::$lockFileHandles[realpath($lockFilePath)] = $resource;
215 $lockFilePath = realpath($lockFilePath);
216 if($lockFilePath ===
false){
217 throw new \InvalidArgumentException(
"Invalid lock file path");
219 if(isset(self::$lockFileHandles[$lockFilePath])){
220 flock(self::$lockFileHandles[$lockFilePath], LOCK_UN);
221 fclose(self::$lockFileHandles[$lockFilePath]);
222 unset(self::$lockFileHandles[$lockFilePath]);
223 @unlink($lockFilePath);
238 public static function safeFilePutContents(
string $fileName,
string $contents,
int $flags = 0, $context =
null) : void{
239 $directory = dirname($fileName);
240 if(!is_dir($directory)){
241 throw new \RuntimeException(
"Target directory path does not exist or is not a directory");
243 if(is_dir($fileName)){
244 throw new \RuntimeException(
"Target file path already exists and is not a file");
250 $temporaryFileName = $fileName .
".$counter.tmp";
252 }
while(is_dir($temporaryFileName));
255 ErrorToExceptionHandler::trap(fn() => $context !==
null ?
256 file_put_contents($temporaryFileName, $contents, $flags, $context) :
257 file_put_contents($temporaryFileName, $contents, $flags)
259 }
catch(\ErrorException $filePutContentsException){
261 @unlink($temporaryFileName, $context) :
262 @unlink($temporaryFileName);
263 throw new \RuntimeException(
"Failed to write to temporary file $temporaryFileName: " . $filePutContentsException->getMessage(), 0, $filePutContentsException);
266 $renameTemporaryFileResult = $context !==
null ?
267 @rename($temporaryFileName, $fileName, $context) :
268 @rename($temporaryFileName, $fileName);
269 if(!$renameTemporaryFileResult){
285 ErrorToExceptionHandler::trap(fn() => $context !==
null ?
286 copy($temporaryFileName, $fileName, $context) :
287 copy($temporaryFileName, $fileName)
289 }
catch(\ErrorException $copyException){
290 throw new \RuntimeException(
"Failed to move temporary file contents into target file: " . $copyException->getMessage(), 0, $copyException);
292 @unlink($temporaryFileName);