Browse Source

add mkdir, delete and copy (ongoing) methods

olinox14 1 year ago
parent
commit
53c31e2219
5 changed files with 224 additions and 17 deletions
  1. 2 0
      readme.md
  2. 16 0
      src/Exception/FileExistsException.php
  3. 16 0
      src/Exception/FileNotFoundException.php
  4. 68 17
      src/Path.php
  5. 122 0
      tests/PathTest.php

+ 2 - 0
readme.md

@@ -20,4 +20,6 @@
 
 Se placer dans le docker, puis : 
 
+    docker start path
+    docker exec -it path bash
     vendor/bin/phpunit .

+ 16 - 0
src/Exception/FileExistsException.php

@@ -0,0 +1,16 @@
+<?php
+
+namespace Path\Exception;
+
+class FileExistsException extends \Exception
+{
+    public function __construct($message = "File already exists", $code = 0, \Exception $previous = null)
+    {
+        parent::__construct($message, $code, $previous);
+    }
+
+    public function __toString()
+    {
+        return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
+    }
+}

+ 16 - 0
src/Exception/FileNotFoundException.php

@@ -0,0 +1,16 @@
+<?php
+
+namespace Path\Exception;
+
+class FileNotFoundException extends \Exception
+{
+    public function __construct($message = "File not found", $code = 0, \Exception $previous = null)
+    {
+        parent::__construct($message, $code, $previous);
+    }
+
+    public function __toString()
+    {
+        return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
+    }
+}

+ 68 - 17
src/Path.php

@@ -3,6 +3,8 @@
 namespace Path;
 
 use InvalidArgumentException;
+use Path\Exception\FileExistsException;
+use Path\Exception\FileNotFoundException;
 use Path\Path\RecursiveDirectoryIterator;
 use Path\Path\RecursiveIteratorIterator;
 use function Path\Path\lchmod;
@@ -58,6 +60,38 @@ class Path
         return $path;
     }
 
+    /**
+     * Copies a directory and its contents recursively from the source directory to the destination directory.
+     *
+     * @param string|self $src The source directory to be copied. It can be a string representing the directory path
+     *                         or an instance of the same class.
+     * @param string|self $dst The destination directory where the source directory and its contents will be copied.
+     *                         It can be a string representing the directory path or an instance of the same class.
+     *
+     * @return void
+     * TODO: see https://stackoverflow.com/a/12763962/4279120
+     */
+    private static function copy_dir(string|self $src, string|self $dst): void
+    {
+        // TODO: review
+        $src = (string)$src;
+        $dst = (string)$dst;
+
+        $dir = opendir($src);
+        @mkdir($dst);
+        while(( $file = readdir($dir)) !== false) {
+            if (( $file != '.' ) && ( $file != '..' )) {
+                if ( is_dir($src . '/' . $file) ) {
+                    self::copy_dir($src . '/' . $file, $dst . '/' . $file);
+                }
+                else {
+                    copy($src . '/' . $file, $dst . '/' . $file);
+                }
+            }
+        }
+        closedir($dir);
+    }
+
     public function __construct(string $path)
     {
         $this->path = $path;
@@ -81,12 +115,12 @@ class Path
     /**
      * Checks if the given path is equal to the current path.
      *
-     * @param string $path The path to compare against.
+     * @param string|Path $path The path to compare against.
      *
      * @return bool Returns true if the given path is equal to the current path, false otherwise.
      */
-    public function eq(string $path): bool {
-        return $path === $this->path;
+    public function eq(string|self $path): bool {
+        return (string)$path === $this->path;
     }
 
     /**
@@ -208,41 +242,58 @@ class Path
      * @param bool $recursive (optional) Indicates whether to create parent directories if they do not exist. Default is false.
      *
      * @return void
+     * @throws FileExistsException
      */
-    public function mkdir($mode = 0777, $recursive = false): void
+    public function mkdir(int $mode = 0777, bool $recursive = false): void
     {
-        if (!file_exists($this->path)) {
-            mkdir($this->path, $mode, $recursive);
+        // 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);
+            } else {
+                return;
+            }
         }
+        if ($this->isFile()) {
+            throw new FileExistsException("A file with this name already exists : " . $this);
+        }
+
+        mkdir($this->path, $mode, $recursive);
     }
 
     /**
      * Deletes a file or a directory.
      *
      * @return void
+     * @throws FileNotFoundException
      */
     public function delete(): void
     {
-        if (is_file($this->path)) {
-            unlink($this->path); //for file
-        } else if (is_dir($this->path)) {
-            rmdir($this->path); //for directory
+        if ($this->isFile()) {
+            unlink($this->path);
+        } else if ($this->isDir()) {
+            rmdir($this->path);
+        } else {
+            throw new FileNotFoundException("File does not exist : " . $this);
         }
     }
 
     /**
      * Copies a file to a specified destination.
      *
-     * @param string $destination The path to the destination file or directory to copy to.
-     *
+     * @param string|Path $destination The path to the destination file or directory to copy to.
      * @return void
+     * @throws FileNotFoundException
+     * TODO: what about the follow_symlink condition?
      */
-    public function copy($destination): void
+    public function copy(string|self $destination): void
     {
-        if (is_file($this->path)) {
-            copy($this->path, $destination);
-        } else if (is_dir($this->path)) {
-            // Copy dir needs special handling, not covered in this example.
+        if ($this->isFile()) {
+            copy($this->path, (string)$destination);
+        } else if ($this->isDir()) {
+            self::copy_dir($this, $destination);
+        } else {
+            throw new FileNotFoundException("File or dir does not exist : " . $this);
         }
     }
 

+ 122 - 0
tests/PathTest.php

@@ -2,9 +2,12 @@
 
 namespace Path\Tests;
 
+use Path\Exception\FileExistsException;
+use Path\Exception\FileNotFoundException;
 use Path\Path;
 use PHPUnit\Framework\TestCase;
 
+// TODO: tested args should be both typed Path and string
 class PathTest extends TestCase
 {
     const TEMP_TEST_DIR = __DIR__ . "/temp";
@@ -422,5 +425,124 @@ class PathTest extends TestCase
         );
     }
 
+    /**
+     * Test 'Path' class 'mkdir' method to create a directory
+     *
+     * @return void
+     * @throws FileExistsException
+     */
+    public function testMkDir(): void
+    {
+        $path = new Path(self::TEMP_TEST_DIR . "/foo");
+        $path->mkdir();
+        $this->assertTrue($path->isDir());
+    }
+
+    /**
+     * Test 'Path' class 'mkdir' method when directory already exists
+     *
+     * @throws FileExistsException If directory already exists
+     */
+    public function testMkDirExistingDir(): void {
+        mkdir(self::TEMP_TEST_DIR . "/foo");
+        $path = new Path(self::TEMP_TEST_DIR . "/foo");
+
+        $this->expectException(FileExistsException::class);
+        $this->expectExceptionMessage("Directory already exists : " . self::TEMP_TEST_DIR . "/foo");
+        
+        $path->mkdir();
+    }
+
+    /**
+     * Test 'Path' class 'mkdir' method to create a directory with existing directory and recursive option
+     *
+     * @return void
+     * @throws FileExistsException
+     */
+    public function testMkDirExistingDirAndRecursive(): void {
+        mkdir(self::TEMP_TEST_DIR . "/foo");
+        $path = new Path(self::TEMP_TEST_DIR . "/foo");
+
+        $path->mkdir(0777, true);
+        $this->assertTrue($path->isDir());
+    }
+
+    /**
+     * Test 'Path' class 'mkdir' method to create a directory when a file with the same name already exists
+     *
+     * @return void
+     * @throws FileExistsException When a file with the same name already exists
+     */
+    public function testMkDirExistingFile(): void {
+        touch(self::TEMP_TEST_DIR . "/foo");
+        $path = new Path(self::TEMP_TEST_DIR . "/foo");
+
+        $this->expectException(FileExistsException::class);
+        $this->expectExceptionMessage("A file with this name already exists : " . self::TEMP_TEST_DIR . "/foo");
+
+        $path->mkdir();
+    }
+
+    /**
+     * Test 'Path' class 'mkdir' method to create a directory recursively
+     *
+     * @return void
+     * @throws FileExistsException
+     */
+    public function testMkDirRecursive(): void {
+        $path = new Path(self::TEMP_TEST_DIR . "/foo/bar");
+        $path->mkdir(0777, true);
+        $this->assertTrue($path->isDir());
+    }
+
+    /**
+     * Test 'Path' class 'delete' method to delete a file.
+     *
+     * @return void
+     * @throws FileNotFoundException
+     */
+    public function testDeleteFileSuccess(): void
+    {
+        touch(self::TEMP_TEST_DIR . "/foo");
+        $path = new Path(self::TEMP_TEST_DIR . "/foo");
+
+        $this->assertTrue($path->isFile());
+        $path->delete();
+        $this->assertFalse($path->isFile());
+    }
+
+    /**
+     * Test 'Path' class 'delete' method to delete a directory successfully
+     *
+     * @return void
+     * @throws FileNotFoundException
+     */
+    public function testDeleteDirSuccess(): void
+    {
+        mkdir(self::TEMP_TEST_DIR . "/foo");
+        $path = new Path(self::TEMP_TEST_DIR . "/foo");
+
+        $this->assertTrue($path->isDir());
+        $path->delete();
+        $this->assertFalse($path->isDir());
+    }
+
+    /**
+     * Test 'Path' class 'delete' method to delete a non-existing file or dir
+     *
+     * @throws FileNotFoundException When the file does not exist
+     */
+    public function testDeleteNonExistingFile(): void
+    {
+        $path = new Path(self::TEMP_TEST_DIR . "/foo");
+
+        $this->assertFalse($path->isDir());
+
+        $this->expectException(FileNotFoundException::class);
+        $this->expectExceptionMessage("File does not exist : " . self::TEMP_TEST_DIR . "/foo");
+
+        $path->delete();
+    }
+
 
 }