Browse Source

complete first level review

olinox14 1 year ago
parent
commit
3c07aa9b9f
2 changed files with 93 additions and 102 deletions
  1. 5 0
      src/BuiltinProxy.php
  2. 88 102
      src/Path.php

+ 5 - 0
src/BuiltinProxy.php

@@ -143,6 +143,11 @@ class BuiltinProxy
         return dirname($path, $levels);
     }
 
+    public function fnmatch(string $pattern, string $filename): bool
+    {
+        return fnmatch($pattern, $filename);
+    }
+
     public function file_get_contents(string $filename, bool $use_include_path = false, $context = null, int $offset = 0, ?int $length = null): false|string
     {
         return file_get_contents($filename, $use_include_path, $context, $offset, $length);

+ 88 - 102
src/Path.php

@@ -329,65 +329,57 @@ class Path
      * > Thanks to https://stackoverflow.com/users/216254/troex
      * @return self A new instance of the class with the normalized path.
      */
-    //TODO: review
+    //TODO: review 2
     public function normPath(): self
     {
-        if (empty($this->path)) {
+        if (empty($path)) {
             return $this->cast('.');
         }
 
-        $initial_slashes =
-            str_starts_with($this->path, '//') ?
-                2 :
-                (int)str_starts_with($this->path, '/');
+        $initialSlashes = str_starts_with($path, '//')
+            ? 2
+            : (str_starts_with($path, '/') ? 1 : 0);
 
-        $comps = explode('/', $this->path);
-        $new_comps = [];
+        $parts = explode('/', $path);
+        $newParts = [];
 
-        foreach ($comps as $comp)
+        foreach ($parts as $part)
         {
-            if (in_array($comp, array('', '.'))) {
+            if (!$part || $part === '.') {
                 continue;
             }
 
             if (
-                $comp != '..' ||
-                !$initial_slashes &&
-                !$new_comps ||
-                $new_comps &&
-                (end($new_comps) == '..')
+                ($part != '..') ||
+                (!$initialSlashes && !$newParts) ||
+                ($newParts && (end($newParts) == '..'))
             ) {
-                $new_comps[] = $comp;
+                $newParts[] = $part;
             }
-            elseif ($new_comps) {
+            elseif ($newParts) {
                 array_pop($new_comps);
             }
         }
 
-        $comps = $new_comps;
-        $path = implode('/', $comps);
+        $path = implode('/', $newParts);
+        if ($initialSlashes)
+            $path = str_repeat('/', $initialSlashes) . $path;
 
-        if ($initial_slashes) {
-            $path = str_repeat('/', $initial_slashes) . $path;
-        }
-
-        return $this->cast($path || '.');
+        return $this->cast($path);
     }
 
     /**
      * Creates a new directory.
      *
-     * @param int $mode (optional) The permissions for the new directory. Default is 0777.
-     * @param bool $recursive (optional) Indicates whether to create parent directories if they do not exist. Default is false.
+     * @param int $mode The permissions for the new directory. Default is 0777.
+     * @param bool $recursive Indicates whether to create parent directories if they do not exist. Default is false.
      *
      * @return void
      * @throws FileExistsException
      * @throws IOException
      */
-    //TODO: review
     public function mkdir(int $mode = 0777, bool $recursive = false): void
     {
-        // TODO: may we make $mode the second arg, and mimic the mode of the parent if not provided?
         if ($this->isDir()) {
             if (!$recursive) {
                 throw new FileExistsException("Directory already exists : " . $this);
@@ -395,6 +387,7 @@ class Path
                 return;
             }
         }
+
         if ($this->isFile()) {
             throw new FileExistsException("A file with this name already exists : " . $this);
         }
@@ -413,7 +406,6 @@ class Path
      * @throws FileNotFoundException
      * @throws IOException
      */
-    //TODO: review
     public function delete(): void
     {
         if ($this->isFile()) {
@@ -429,7 +421,7 @@ class Path
                 throw new IOException("Error why deleting directory : " . $this->path);
             }
         } else {
-            throw new FileNotFoundException("File does not exist : " . $this);
+            throw new FileNotFoundException("File or directory does not exist : " . $this);
         }
     }
 
@@ -444,7 +436,7 @@ class Path
      * @throws FileExistsException
      * @throws IOException
      */
-    //TODO: review
+    //TODO: review2
     public function copy(string|self $destination, bool $follow_symlinks = false): self
     {
         if (!$this->isFile()) {
@@ -478,7 +470,7 @@ class Path
      * @throws FileNotFoundException If the source file or directory does not exist.
      * @throws IOException
      */
-    //TODO: review
+    //TODO: review2
     public function copyTree(string|self $destination, bool $follow_symlinks = false): self
     {
         // TODO: voir à faire la synthèse de copytree et https://path.readthedocs.io/en/latest/api.html#path.Path.merge_tree
@@ -515,7 +507,7 @@ class Path
      * @throws FileExistsException
      * @throws IOException
      */
-    //TODO: review
+    //TODO: review2
     public function move(string|self $destination): self
     {
         // TODO: comparer à https://path.readthedocs.io/en/latest/api.html#path.Path.move
@@ -547,7 +539,6 @@ class Path
      * @return void
      * @throws IOException
      */
-    //TODO: review
     public function touch(int|\DateTime $time = null, int|\DateTime $atime = null): void
     {
         if ($time instanceof \DateTime) {
@@ -571,7 +562,6 @@ class Path
      * @throws FileNotFoundException
      * @throws IOException
      */
-    //TODO: review
     public function size(): int
     {
         if (!$this->isFile()) {
@@ -592,40 +582,33 @@ class Path
      *
      * @return self The parent directory of the specified path.
      */
-    //TODO: review
-    public function parent(): self
+    public function parent(int $levels = 1): self
     {
-        // TODO: check on special cases
-        // TODO: add the levels argument?
         return $this->cast(
-            $this->builtin->dirname($this->path)
+            $this->builtin->dirname($this->path ?? ".", $levels)
         );
     }
 
     /**
      * Alias for Path->parent() method
      *
+     * @param int $levels
      * @return self
      */
-    //TODO: review
-    public function dirname(): self
+    public function dirname(int $levels = 1): self
     {
-        // TODO: add the levels argument?
-        return $this->parent();
+        return $this->parent($levels);
     }
 
     /**
-     * List of this directory’s subdirectories.
+     * Retrieves an array of this directory’s subdirectories.
      *
      * The elements of the list are Path objects.
-     * This does not walk recursively into subdirectories (but see walkdirs()).
-     *
-     * Accepts parameters to iterdir().
+     * This does not walk recursively into subdirectories (but see walkdirs() //TODO: implement).
      *
      * @return array
      * @throws FileNotFoundException
      */
-    //TODO: review
     public function dirs(): array
     {
         if (!$this->builtin->is_dir($this->path)) {
@@ -635,8 +618,9 @@ class Path
         $dirs = [];
 
         foreach ($this->builtin->scandir($this->path) as $filename) {
-            if ('.' === $filename) continue;
-            if ('..' === $filename) continue;
+            if ($filename === '.' || $filename === '..') {
+                continue;
+            }
 
             if ($this->builtin->is_dir(self::join($this->path, $filename))) {
                 $dirs[] = $filename;
@@ -652,7 +636,6 @@ class Path
      * @return array An array of files present in the directory.
      * @throws FileNotFoundException If the directory specified in the path does not exist.
      */
-    //TODO: review
     public function files(): array
     {
         if (!$this->builtin->is_dir($this->path)) {
@@ -662,8 +645,9 @@ class Path
         $files = [];
 
         foreach ($this->builtin->scandir($this->path) as $filename) {
-            if ('.' === $filename) continue;
-            if ('..' === $filename) continue;
+            if ($filename === '.' || $filename === '..') {
+                continue;
+            }
 
             if ($this->builtin->is_file(self::join($this->path, $filename))) {
                 $files[] = $filename;
@@ -673,19 +657,24 @@ class Path
         return $files;
     }
 
-    public function fnmatch()
+    /**
+     * Performs a pattern matching using the `fnmatch()` function.
+     *
+     * @param string $pattern The pattern to match against.
+     * @return bool True if the path matches the pattern, false otherwise.
+     */
+    public function fnmatch(string $pattern): bool
     {
-        // TODO: implement https://path.readthedocs.io/en/latest/api.html#path.Path.fnmatch
+        return $this->builtin->fnmatch($pattern, $this->path);
     }
 
     /**
      * Retrieves the content of a file.
      *
-     * @return bool|string The content of the file as a string.
+     * @return string The content of the file as a string.
      * @throws FileNotFoundException|IOException
      */
-    //TODO: review
-    public function getContent(): bool|string
+    public function getContent(): string
     {
         if (!$this->builtin->is_file($this->path)) {
             throw new FileNotFoundException("File does not exist : " . $this->path);
@@ -694,7 +683,7 @@ class Path
         $text = $this->builtin->file_get_contents($this->path);
 
         if ($text === false) {
-            throw new IOException("Error reading file {$this->path}");
+            throw new IOException("An error occurred while reading file {$this->path}");
         }
         return $text;
     }
@@ -704,11 +693,11 @@ class Path
      *
      * @param string $content The contents to be written to the file.
      * @param bool $append
-     * @return int
+     * @return int The number of bytes that were written to the file
      * @throws FileNotFoundException
      * @throws IOException
      */
-    //TODO: review
+    //TODO: review2
     public function putContent(string $content, bool $append = false): int
     {
         if (!$this->builtin->is_file($this->path)) {
@@ -732,13 +721,18 @@ class Path
     }
 
     /**
-     * @throws IOException
+     * Writes an array of lines to a file.
+     *
+     * @param array<string> $lines An array of lines to be written to the file.
+     * @return int The number of bytes written to the file.
      * @throws FileNotFoundException
+     * @throws IOException
      */
-    //TODO: review
     public function putLines(array $lines): int
     {
-        return $this->putContent(implode(PHP_EOL, $lines));
+        return $this->putContent(
+            implode(PHP_EOL, $lines)
+        );
     }
 
     /**
@@ -748,10 +742,9 @@ class Path
      * @throws FileNotFoundException
      * @throws IOException
      */
-    //TODO: review
     public function getPermissions(): int
     {
-        if (!$this->isFile()) {
+        if (!$this->exists()) {
             throw new FileNotFoundException("File or dir does not exist : " . $this->path);
         }
 
@@ -773,10 +766,9 @@ class Path
      * @throws FileNotFoundException
      * @throws IOException
      */
-    //TODO: review
     public function setPermissions(int $permissions): void
     {
-        if (!$this->isFile()) {
+        if (!$this->exists()) {
             throw new FileNotFoundException("File or dir does not exist : " . $this->path);
         }
         $this->builtin->clearstatcache(); // TODO: check for a better way of dealing with PHP cache
@@ -796,10 +788,9 @@ class Path
      * @throws FileNotFoundException
      * @throws IOException
      */
-    //TODO: review
     public function setOwner(string $user, string $group): void
     {
-        if (!$this->isFile()) {
+        if (!$this->exists()) {
             throw new FileNotFoundException("File or dir does not exist : " . $this->path);
         }
 
@@ -836,7 +827,6 @@ class Path
      *
      * @return bool Returns true if the file exists, false otherwise.
      */
-    //TODO: review
     public function exists(): bool
     {
         return $this->builtin->file_exists($this->path);
@@ -870,7 +860,6 @@ class Path
      * @throws FileNotFoundException
      * @throws IOException
      */
-    //TODO: review
     public function glob(string $pattern): array
     {
         if (!$this->isDir()) {
@@ -882,11 +871,11 @@ class Path
         $result = $this->builtin->glob($pattern);
 
         if ($result === false) {
-            throw new IOException("Error while getting blob on " . $this->path);
+            throw new IOException("Error while getting glob on " . $this->path);
         }
 
         return array_map(
-            function (string $s) { return new static($s); },
+            function (string $s) { return new static(self::join($this->path, $s)); },
             $result
         );
     }
@@ -905,7 +894,7 @@ class Path
      *
      * @return bool True if the directory was successfully removed, false otherwise.
      */
-    //TODO: review
+    //TODO: review2
     protected function rrmdir(): bool
     {
         if (!is_dir($this->path)) {
@@ -928,12 +917,12 @@ class Path
     }
 
     /**
-     * Removes a directory and its contents recursively.
+     * Removes a directory and its contents.
      *
      * @throws FileNotFoundException
      * @throws IOException
      */
-    //TODO: review
+    //TODO: review2
     public function rmdir(bool $recursive = false): void
     {
         if (!$this->isDir()) {
@@ -996,16 +985,15 @@ class Path
      * @throws FileNotFoundException If the path does not refer to a file.
      * @throws IOException If the file fails to open.
      */
-    //TODO: review
     public function open(string $mode = 'r'): mixed
     {
         if (!$this->isFile()) {
-            throw new FileNotFoundException("{$this->path} is not a file");
+            throw new FileNotFoundException($this->path . " is not a file");
         }
 
         $handle = $this->builtin->fopen($this->path, $mode);
         if ($handle === false) {
-            throw new IOException("Failed opening file {$this->path}");
+            throw new IOException("Failed opening file " . $this->path);
         }
 
         return $handle;
@@ -1018,7 +1006,6 @@ class Path
      * @param string $mode The mode in which to open the file. Defaults to 'r'.
      * @throws Throwable If an exception is thrown within the callback function.
      */
-    //TODO: review
     public function with(callable $callback, string $mode = 'r'): mixed
     {
         $handle = $this->open($mode);
@@ -1027,7 +1014,7 @@ class Path
         } finally {
             $closed = $this->builtin->fclose($handle);
             if (!$closed) {
-                throw new IOException("Could not close the file stream : " .$this->path);
+                throw new IOException("Could not close the file stream : " . $this->path);
             }
         }
     }
@@ -1041,7 +1028,6 @@ class Path
      * @throws IOException
      * @throws Throwable
      */
-    //TODO: review
     public function chunks(int $chunk_size = 8192): Generator
     {
         $handle = $this->open('rb');
@@ -1052,7 +1038,7 @@ class Path
         } finally {
             $closed = $this->builtin->fclose($handle);
             if (!$closed) {
-                throw new IOException("Could not close the file stream : " .$this->path);
+                throw new IOException("Could not close the file stream : " . $this->path);
             }
         }
     }
@@ -1062,7 +1048,6 @@ class Path
      *
      * @return bool
      */
-    //TODO: review
     public function isAbs(): bool
     {
         return str_starts_with($this->path, '/');
@@ -1075,7 +1060,6 @@ class Path
      * @param int $mode The new permissions (octal).
      * @throws FileNotFoundException|IOException
      */
-    //TODO: review
     public function chmod(int $mode): void
     {
         $this->setPermissions($mode);
@@ -1089,7 +1073,6 @@ class Path
      * @param string $group The new owner group name.
      * @throws FileNotFoundException|IOException
      */
-    //TODO: review
     public function chown(string $user, string $group): void
     {
         $this->setOwner($user, $group);
@@ -1099,10 +1082,14 @@ class Path
      * Changes the root directory of the current process to the specified directory.
      *
      * @throws IOException
+     * @throws FileNotFoundException
      */
-    //TODO: review
     public function chroot(): void
     {
+        if (!$this->isDir()) {
+            throw new FileNotFoundException("Dir does not exist : " . $this->path);
+        }
+
         $success = $this->builtin->chroot($this->path);
         if (!$success) {
             throw new IOException("Error changing root directory to " . $this->path);
@@ -1114,7 +1101,6 @@ class Path
      *
      * @return bool
      */
-    //TODO: review
     public function isLink(): bool
     {
         return $this->builtin->is_link($this->path);
@@ -1125,6 +1111,10 @@ class Path
         // TODO: implement https://path.readthedocs.io/en/latest/api.html#path.Path.ismount
     }
 
+    /**
+     * TODO: review2
+     * @return \DirectoryIterator
+     */
     protected function getDirectoryIterator(): \DirectoryIterator
     {
         // TODO: make it public?
@@ -1135,13 +1125,13 @@ class Path
      * Iterate over the files in this directory.
      *
      * @return Generator
-     * @throws FileNotFoundException if the path is not a directory.
+     * @throws FileNotFoundException If the path is not a directory.
      */
-    //TODO: review
+    //TODO: review2
     public function iterDir(): Generator
     {
         if (!$this->isDir()) {
-            throw new FileNotFoundException("{$this->path} is not a directory");
+            throw new FileNotFoundException($this->path . " is not a directory");
         }
 
         foreach ($this->getDirectoryIterator() as $fileInfo) {
@@ -1159,24 +1149,22 @@ class Path
     }
 
     /**
-     * Create a hard link pointing to a path.
+     * Create a hard link pointing to this path.
      *
      * @param string|Path $target
      * @throws FileExistsException
      * @throws FileNotFoundException
      * @throws IOException
      */
-    //TODO: review
     public function link(string|self $target): void
     {
-        // TODO: manage dirs and files here
-        if (!$this->isFile()) {
-            throw new FileNotFoundException("{$this->path} is not a file");
+        if (!$this->exists()) {
+            throw new FileNotFoundException("File or dir does not exist : " . $this);
         }
 
         $target = $this->cast($target);
 
-        if ($target->isFile()) {
+        if ($target->exists()) {
             throw new FileExistsException($target . " already exist");
         }
 
@@ -1193,7 +1181,6 @@ class Path
      * @return array
      * @throws IOException
      */
-    //TODO: review
     public function lstat(): array
     {
         $result = $this->builtin->lstat($this->path);
@@ -1233,7 +1220,6 @@ class Path
      *
      * @return array
      */
-    //TODO: review
     public function parts(): array
     {
         $parts = [];
@@ -1252,7 +1238,7 @@ class Path
      * @throws FileNotFoundException
      * @throws IOException
      */
-    //TODO: review
+    //TODO: review2
     public function getRelativePath(string|self $basePath): string
     {
         if (!$this->exists()) {