friendship ended with social-app. php is my new best friend
1<?php
2/**
3 * Class Directory
4 *
5 * @created 29.10.2024
6 * @author smiley <smiley@chillerlan.net>
7 * @copyright 2024 smiley
8 * @license MIT
9 */
10declare(strict_types=1);
11
12namespace chillerlan\Utilities;
13
14use InvalidArgumentException;
15use RuntimeException;
16use function clearstatcache;
17use function dirname;
18use function file_exists;
19use function is_dir;
20use function is_file;
21use function is_readable;
22use function is_writable;
23use function mkdir;
24use function rmdir;
25use function sprintf;
26use function str_replace;
27use function trim;
28use const DIRECTORY_SEPARATOR;
29
30/**
31 * Basic directory utilities
32 */
33final class Directory{
34
35 /**
36 * Checks whether a directory exists
37 *
38 * @codeCoverageIgnore
39 */
40 public static function exists(string $dir):bool{
41 return file_exists($dir) && is_dir($dir);
42 }
43
44 /**
45 * Checks whether the given directory is readable
46 *
47 * @codeCoverageIgnore
48 */
49 public static function isReadable(string $dir):bool{
50 return self::exists($dir) && is_readable($dir);
51 }
52
53 /**
54 * Checks whether the given directory is writable
55 *
56 * @codeCoverageIgnore
57 */
58 public static function isWritable(string $dir):bool{
59 return self::exists($dir) && is_writable($dir);
60 }
61
62 /**
63 * Creates a directory
64 *
65 * @throws \InvalidArgumentException|\RuntimeException
66 */
67 public static function create(string $dir, int $permissions = 0o777, bool $recursive = true):string{
68 $dir = trim($dir);
69
70 if($dir === ''){
71 throw new InvalidArgumentException('invalid directory');
72 }
73
74 // $dir exists but is not a directory
75 if(file_exists($dir) && !is_dir($dir)){
76 throw new InvalidArgumentException(sprintf('cannot create directory: %s already exists as a file or link', $dir));
77 }
78
79 // $dir doesn't exist and the attempt to create failed
80 if(!file_exists($dir) && !mkdir($dir, $permissions, $recursive)){
81 throw new RuntimeException(sprintf('could not create directory: %s', $dir)); // @codeCoverageIgnore
82 }
83
84 clearstatcache();
85
86 return File::realpath($dir);
87 }
88
89 /**
90 * Removes a directory
91 */
92 public static function remove(string $dir):bool{
93
94 if($dir === '' || !self::isWritable($dir)){
95 throw new InvalidArgumentException('invalid directory');
96 }
97
98 if(!rmdir($dir)){
99 throw new RuntimeException('could not delete the given directory'); // @codeCoverageIgnore
100 }
101
102 clearstatcache();
103
104 return true;
105 }
106
107 /**
108 * Returns the relative path from the given directory (realpath)
109 */
110 public static function relativePath(string $path, string $from, string $separator = DIRECTORY_SEPARATOR):string{
111 $path = File::realpath($path);
112 $from = File::realpath($from);
113
114 if(is_file($path)){
115 $path = dirname($path);
116 }
117
118 if(is_file($from)){
119 $from = dirname($from);
120 }
121
122 return trim(str_replace([$from, '\\', '/'], ['', $separator, $separator], $path), '\\/');
123 }
124
125}