Browse Source

prepare a definitive class signature

olinox14 1 year ago
parent
commit
6374640bb3
1 changed files with 357 additions and 15 deletions
  1. 357 15
      src/Path.php

+ 357 - 15
src/Path.php

@@ -37,6 +37,11 @@ class Path
 
     protected mixed $handle;
 
+    public static function here(): self
+    {
+        return new self(__DIR__);
+    }
+
     /**
      * Joins two or more parts of a path together, inserting '/' as needed.
      * If any component is an absolute path, all previous path components
@@ -66,10 +71,6 @@ class Path
         return $path;
     }
 
-    public function withFile(string|self $path, string $mode = 'r') {
-        //TODO: do a 'with open' like method
-    }
-
     /**
      * Copies a directory and its contents recursively from the source directory to the destination directory.
      *
@@ -110,6 +111,7 @@ class Path
      * @param string $dst The path to the destination directory.
      * @return void
      * @throws FileNotFoundException If a file within the source directory does not exist.
+     * @throws IOException
      */
     private static function _copy_dir(string $src, string $dst): void
     {
@@ -129,7 +131,10 @@ class Path
                 if (is_dir($path)) {
                     self::_copy_dir($path, $newPath);
                 } else if(is_file($path)) {
-                    copy($path, $newPath);
+                    $success = copy($path, $newPath);
+                    if (!$success) {
+                        throw new IOException("Error copying file {$path} to {$newPath}");
+                    }
                 } else {
                     throw new FileNotFoundException("File does not exist : " . $path);
                 }
@@ -255,6 +260,34 @@ class Path
         return date('Y-m-d H:i:s', $time);
     }
 
+    /**
+     * Retrieves the creation time of a file or directory.
+     *
+     * @return string|null The creation time of the file or directory in 'Y-m-d H:i:s' format, or null if the time could not be retrieved.
+     */
+    function ctime(): ?string
+    {
+        $time = filectime($this->path);
+        if ($time === false) {
+            return null;
+        }
+        return date('Y-m-d H:i:s', $time);
+    }
+
+    /**
+     * Retrieves the last modified time of a file or directory.
+     *
+     * @return string|null The last modified time of the file or directory in the format 'Y-m-d H:i:s', or null if the time cannot be determined.
+     */
+    function mtime(): ?string
+    {
+        $time = filemtime($this->path);
+        if ($time === false) {
+            return null;
+        }
+        return date('Y-m-d H:i:s', $time);
+    }
+
     /**
      * Check if the path refers to a regular file.
      *
@@ -295,6 +328,29 @@ class Path
         return pathinfo($this->path, PATHINFO_BASENAME);
     }
 
+    /**
+     * Changes the current working directory.
+     *
+     * @param string|self $path The path to the directory to change into.
+     *                          It can be either a string containing the path or an instance of the same class.
+     * @return bool True on success, false on failure.
+     */
+    public function cd(string|self $path): bool
+    {
+        return chdir((string)$path);
+    }
+
+    /**
+     * > alias for Path->cd($path)
+     *
+     * @param string|Path $path
+     * @return bool
+     */
+    public function chdir(string|self $path): bool
+    {
+        return $this->cd($path);
+    }
+
     /**
      * Get the name of the file or path.
      *
@@ -305,6 +361,16 @@ class Path
         return pathinfo($this->path, PATHINFO_FILENAME);
     }
 
+    public function normcase()
+    {
+        // TODO: implement https://docs.python.org/3/library/os.path.html#os.path.normcase
+    }
+
+    public function normpath()
+    {
+        // TODO: implement https://docs.python.org/3/library/os.path.html#os.path.normpath
+    }
+
     /**
      * Creates a new directory.
      *
@@ -349,28 +415,72 @@ class Path
     }
 
     /**
-     * Copies a file or directory to the specified destination.
+     * Copy data and mode bits (“cp src dst”). Return the file’s destination.
+     * The destination may be a directory.
+     * If follow_symlinks is false, symlinks won’t be followed. This resembles GNU’s “cp -P src dst”.
      *
-     * @param string|self $destination The destination path or object to copy the file or directory to.
-     * @throws FileNotFoundException If the source file or directory does not exist.
+     * @param string|self $destination The destination path or object to copy the file to.
+     * @throws FileNotFoundException If the source file does not exist or is not a file.
      * @throws FileExistsException
+     * @throws IOException
      */
-    public function copy(string|self $destination): void
+    public function copy(string|self $destination, bool $follow_symlinks = false): self
     {
+        if (!$this->isFile()) {
+            throw new FileNotFoundException("File does not exist or is not a file : " . $this);
+        }
+
+        $destination = (string)$destination;
+        if (is_dir($destination)) {
+            $destination = self::join($destination, $this->basename());
+        }
+
+        if (file_exists($destination)) {
+            throw new FileExistsException("File already exists : " . $destination);
+        }
+
+        $success = copy($this->path, $destination);
+        if (!$success) {
+            throw new IOException("Error copying file {$this->path} to {$destination}");
+        }
+
+        return new self($destination);
+    }
+
+    /**
+     * Copies the content of a file or directory to the specified destination.
+     *
+     * @param string|self $destination The destination path or directory to copy the content to.
+     * @param bool $follow_symlinks (Optional) Whether to follow symbolic links.
+     * @return self The object on which the method is called.
+     * @throws FileExistsException If the destination path or directory already exists.
+     * @throws FileNotFoundException If the source file or directory does not exist.
+     * @throws IOException
+     */
+    public function copy_tree(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
         if ($this->isFile()) {
             $destination = (string)$destination;
             if (is_dir($destination)) {
                 $destination = self::join($destination, $this->basename());
             }
+
             if (file_exists($destination)) {
                 throw new FileExistsException("File or dir already exists : " . $destination);
             }
-            copy($this->path, $destination);
+
+            $success = copy($this->path, $destination);
+            if (!$success) {
+                throw new IOException("Error copying file {$this->path} to {$destination}");
+            }
         } else if ($this->isDir()) {
             self::copy_dir($this, $destination);
         } else {
             throw new FileNotFoundException("File or dir does not exist : " . $this);
         }
+
+        return new self($destination);
     }
 
     /**
@@ -383,6 +493,7 @@ class Path
      */
     public function move(string|self $destination): void
     {
+        // TODO: comparer à https://path.readthedocs.io/en/latest/api.html#path.Path.move
         $destination = (string)$destination;
         if (is_dir($destination)) {
             $destination = self::join($destination, $this->basename());
@@ -439,12 +550,83 @@ class Path
     /**
      * Retrieves the parent directory of a file or directory path.
      *
-     * @return string The parent directory of the specified path.
+     * @return self The parent directory of the specified path.
      */
-    public function parent(): string
+    public function parent(): self
     {
         // TODO: check on special cases
-        return dirname($this->path);
+        return new self(dirname($this->path));
+    }
+
+    /**
+     * Alias for Path->parent() method
+     *
+     * @return self
+     */
+    public function dirname(): self
+    {
+        return $this->parent();
+    }
+
+    /**
+     * List 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().
+     *
+     * @return array
+     * @throws FileNotFoundException
+     */
+    public function dirs(): array
+    {
+        if (!is_dir($this->path)) {
+            throw new FileNotFoundException("Directory does not exist: " . $this->path);
+        }
+
+        $dirs = [];
+
+        foreach (scandir($this->path) as $filename) {
+            if ('.' === $filename) continue;
+            if ('..' === $filename) continue;
+
+            if (is_dir(self::join($this->path, $filename))) {
+                $dirs[] = $filename;
+            }
+        }
+
+        return $dirs;
+    }
+
+    /**
+     * Retrieves an array of files present in the directory.
+     *
+     * @return array An array of files present in the directory.
+     * @throws FileNotFoundException If the directory specified in the path does not exist.
+     */
+    public function files(): array
+    {
+        if (!is_dir($this->path)) {
+            throw new FileNotFoundException("Directory does not exist: " . $this->path);
+        }
+
+        $files = [];
+
+        foreach (scandir($this->path) as $filename) {
+            if ('.' === $filename) continue;
+            if ('..' === $filename) continue;
+
+            if (is_file(self::join($this->path, $filename))) {
+                $files[] = $filename;
+            }
+        }
+
+        return $files;
+    }
+
+    public function fnmatch()
+    {
+        // TODO: implement https://path.readthedocs.io/en/latest/api.html#path.Path.fnmatch
     }
 
     /**
@@ -465,6 +647,11 @@ class Path
         return $text;
     }
 
+    public function getOwner()
+    {
+        // TODO:  implement https://path.readthedocs.io/en/latest/api.html#path.Path.get_owner
+    }
+
     /**
      * Writes contents to a file.
      *
@@ -479,6 +666,14 @@ class Path
         file_put_contents($this->path, $content);
     }
 
+    public function putLines(array $lines): void
+    {
+        // TODO: review use-cases
+        // TODO: complete the input types
+        // TODO: add a condition on the creation of the file if not existing
+        file_put_contents($this->path, implode(PHP_EOL, $lines));
+    }
+
     /**
      * Appends contents to a file.
      *
@@ -545,6 +740,23 @@ class Path
             chgrp($this->path, $group);
     }
 
+    public function setATime()
+    {
+        // TODO: implement
+    }
+    public function setCTime()
+    {
+        // TODO: implement
+    }
+    public function setMTime()
+    {
+        // TODO: implement
+    }
+    public function setUTime()
+    {
+        // TODO: implement
+    }
+
     /**
      * Checks if a file exists.
      *
@@ -555,6 +767,26 @@ class Path
         return file_exists($this->path);
     }
 
+    public function samefile()
+    {
+        // TODO: implement https://path.readthedocs.io/en/latest/api.html#path.Path.samefile
+    }
+
+    public function expand()
+    {
+        // TODO: implement https://path.readthedocs.io/en/latest/api.html#path.Path.expand
+    }
+
+    public function expand_user()
+    {
+        // TODO: implement https://path.readthedocs.io/en/latest/api.html#path.Path.expanduser
+    }
+
+    public function expand_vars()
+    {
+        // TODO: implement https://path.readthedocs.io/en/latest/api.html#path.Path.expandvars
+    }
+
     /**
      * Retrieves a list of files and directories that match a specified pattern.
      *
@@ -568,6 +800,15 @@ class Path
         }
     }
 
+    public function remove()
+    {
+        // TODO: implement https://path.readthedocs.io/en/latest/api.html#path.Path.remove
+    }
+    public function remove_p()
+    {
+        // TODO: implement https://path.readthedocs.io/en/latest/api.html#path.Path.remove_p
+    }
+
     /**
      * Removes a directory and its contents recursively.
      *
@@ -586,6 +827,46 @@ class Path
         }
     }
 
+    public function rename()
+    {
+        // TODO: implement https://path.readthedocs.io/en/latest/api.html#path.Path.rename
+    }
+
+    public function renames()
+    {
+        // TODO: implement https://path.readthedocs.io/en/latest/api.html#path.Path.renames
+    }
+
+    public function read_hash()
+    {
+        // TODO: implement https://path.readthedocs.io/en/latest/api.html#path.Path.read_hash
+    }
+
+    public function read_hexhash()
+    {
+        // TODO: implement https://path.readthedocs.io/en/latest/api.html#path.Path.read_hexhash
+    }
+
+    public function read_md5()
+    {
+        // TODO: implement https://path.readthedocs.io/en/latest/api.html#path.Path.read_md5
+    }
+
+    public function read_text()
+    {
+        // TODO: implement https://path.readthedocs.io/en/latest/api.html#path.Path.read_text
+    }
+
+    public function readlink()
+    {
+        // TODO: implement https://path.readthedocs.io/en/latest/api.html#path.Path.readlink
+    }
+
+    public function readlinkabs()
+    {
+        // TODO: implement https://path.readthedocs.io/en/latest/api.html#path.Path.readlinkabs
+    }
+
     /**
      * Opens a file in the specified mode.
      *
@@ -615,11 +896,32 @@ 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.
      */
-    public function with(callable $callback, string $mode = 'r'): void
+    public function with(callable $callback, string $mode = 'r')
     {
         $handle = $this->open($mode);
         try {
-            $callback($handle);
+            return $callback($handle);
+        } finally {
+            fclose($handle);
+        }
+    }
+
+    /**
+     * Retrieves chunks of data from the file.
+     *
+     * @param callable $callback The callback function to process each chunk of data.
+     * @param int $chunk_size The size of each chunk in bytes. Defaults to 8192.
+     * @return Generator Returns a generator that yields each chunk of data read from the file.
+     * @throws FileNotFoundException
+     * @throws IOException
+     */
+    public function chunks(callable $callback, int $chunk_size = 8192): Generator
+    {
+        $handle = $this->open('rb');
+        try {
+            while (!feof($handle)) {
+                yield fread($handle, $chunk_size);
+            }
         } finally {
             fclose($handle);
         }
@@ -655,12 +957,23 @@ class Path
      * @param string $user The new owner username.
      * @param string $group The new owner group name.
      * @return bool
+     * @throws FileNotFoundException
      */
     public function chown(string $user, string $group): bool
     {
         return $this->setOwner($user, $group);
     }
 
+    /**
+     * Changes the root directory of the current process to the specified directory.
+     *
+     * @return bool Returns true on success or false on failure.
+     */
+    public function chroot(): bool
+    {
+        return chroot($this->path);
+    }
+
     /**
      * Checks if the file is a symbolic link.
      *
@@ -671,6 +984,11 @@ class Path
         return is_link($this->path);
     }
 
+    public function isMount()
+    {
+        // TODO: implement https://path.readthedocs.io/en/latest/api.html#path.Path.ismount
+    }
+
     /**
      * Iterate over the files in this directory.
      *
@@ -692,6 +1010,11 @@ class Path
         }
     }
 
+    public function lines()
+    {
+        // TODO: implement https://path.readthedocs.io/en/latest/api.html#path.Path.lines
+    }
+
     /**
      * Create a hard link pointing to a path.
      *
@@ -717,6 +1040,25 @@ class Path
         return lstat($this->path);
     }
 
+    public function splitDrive()
+    {
+        // TODO: implement https://path.readthedocs.io/en/latest/api.html#path.Path.splitdrive
+    }
+
+    public function stat() {
+        // TODO: implement https://path.readthedocs.io/en/latest/api.html#path.Path.stat
+    }
+
+    public function symlink()
+    {
+        // TODO: implement https://path.readthedocs.io/en/latest/api.html#path.Path.symlink
+    }
+
+    public function unlink()
+    {
+        // TODO: implement https://path.readthedocs.io/en/latest/api.html#path.Path.unlink
+    }
+
     /**
      * Returns the individual parts of this path.
      * The eventual leading directory separator is kept.