Skip to content

Commit 1ced66d

Browse files
committed
add permission get/set feature interfaces, implement for file system
1 parent e749a23 commit 1ced66d

13 files changed

+311
-27
lines changed

src/Exception/ChmodException.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Aternos\IO\Exception;
4+
5+
/**
6+
* Class ChmodException
7+
*
8+
* Exception thrown when a chmod operation fails
9+
*
10+
* @package Aternos\IO\Exception
11+
*/
12+
class ChmodException extends IOException
13+
{
14+
15+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace Aternos\IO\Interfaces\Features;
4+
5+
use Aternos\IO\Exception\IOException;
6+
use Aternos\IO\Interfaces\IOElementInterface;
7+
use Aternos\IO\Interfaces\Util\PermissionsInterface;
8+
9+
/**
10+
* Interface GetPermissionsInterface
11+
*
12+
* Allows getting the permissions of an element
13+
*
14+
* @package Aternos\IO\Interfaces\Features
15+
*/
16+
interface GetPermissionsInterface extends IOElementInterface
17+
{
18+
/**
19+
* Get the permissions of this element
20+
*
21+
* @throws IOException
22+
* @return PermissionsInterface
23+
*/
24+
public function getPermissions(): PermissionsInterface;
25+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace Aternos\IO\Interfaces\Features;
4+
5+
use Aternos\IO\Exception\IOException;
6+
use Aternos\IO\Interfaces\IOElementInterface;
7+
use Aternos\IO\Interfaces\Util\PermissionsInterface;
8+
9+
/**
10+
* Interface SetPermissionsInterface
11+
*
12+
* Allows setting the permissions of an element
13+
*
14+
* @package Aternos\IO\Interfaces\Features
15+
*/
16+
interface SetPermissionsInterface extends IOElementInterface
17+
{
18+
/**
19+
* Set the permissions of this element
20+
*
21+
* @throws IOException
22+
* @param PermissionsInterface $permissions
23+
* @return $this
24+
*/
25+
public function setPermissions(PermissionsInterface $permissions): static;
26+
}

src/Interfaces/Util/PermissionsClassInterface.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,11 @@ public function canExecute(): bool;
5555
* @return $this
5656
*/
5757
public function setExecute(bool $execute): static;
58+
59+
/**
60+
* Get the numeric representation of the class
61+
*
62+
* @return int
63+
*/
64+
public function toNumeric(): int;
5865
}

src/Interfaces/Util/PermissionsInterface.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,11 @@ public function getOther(): PermissionsClassInterface;
5555
* @return $this
5656
*/
5757
public function setOther(PermissionsClassInterface $other): static;
58+
59+
/**
60+
* Get the numeric representation of the permissions
61+
*
62+
* @return int
63+
*/
64+
public function toNumeric(): int;
5865
}

src/System/FilesystemElement.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,19 @@
22

33
namespace Aternos\IO\System;
44

5+
use Aternos\IO\Exception\ChmodException;
56
use Aternos\IO\Exception\MoveException;
67
use Aternos\IO\Exception\PathOutsideElementException;
78
use Aternos\IO\Exception\StatException;
89
use Aternos\IO\Exception\TouchException;
910
use Aternos\IO\Interfaces\Features\GetPathInterface;
11+
use Aternos\IO\Interfaces\Util\PermissionsInterface;
1012
use Aternos\IO\System\Directory\Directory;
1113
use Aternos\IO\System\File\File;
1214
use Aternos\IO\System\Link\DirectoryLink;
1315
use Aternos\IO\System\Link\FileLink;
1416
use Aternos\IO\System\Link\Link;
17+
use Aternos\IO\System\Util\Permissions;
1518

1619
/**
1720
* Class FilesystemElement
@@ -214,6 +217,33 @@ public function setModificationTimestamp(int $timestamp): static
214217
return $this;
215218
}
216219

220+
/**
221+
* @inheritDoc
222+
* @throws StatException
223+
*/
224+
public function getPermissions(): PermissionsInterface
225+
{
226+
$numericPermissions = @fileperms($this->path);
227+
if ($numericPermissions === false) {
228+
$error = error_get_last();
229+
throw new StatException("Could not get permissions (" . $this->path . ")" . ($error ? ": " . $error["message"] : ""), $this);
230+
}
231+
return Permissions::fromNumeric($numericPermissions);
232+
}
233+
234+
/**
235+
* @inheritDoc
236+
* @throws ChmodException
237+
*/
238+
public function setPermissions(PermissionsInterface $permissions): static
239+
{
240+
if (!@chmod($this->path, $permissions->toNumeric())) {
241+
$error = error_get_last();
242+
throw new ChmodException("Could not set permissions (" . $this->path . ")" . ($error ? ": " . $error["message"] : ""), $this);
243+
}
244+
return $this;
245+
}
246+
217247
/**
218248
* @return string[]
219249
*/

src/System/FilesystemInterface.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
use Aternos\IO\Interfaces\Features\ExistsInterface;
77
use Aternos\IO\Interfaces\Features\GetAccessTimestampInterface;
88
use Aternos\IO\Interfaces\Features\GetModificationTimestampInterface;
9+
use Aternos\IO\Interfaces\Features\GetPermissionsInterface;
910
use Aternos\IO\Interfaces\Features\GetStatusChangeTimestampInterface;
1011
use Aternos\IO\Interfaces\Features\MovePathInterface;
1112
use Aternos\IO\Interfaces\Features\SetAccessTimestampInterface;
1213
use Aternos\IO\Interfaces\Features\SetModificationTimestampInterface;
14+
use Aternos\IO\Interfaces\Features\SetPermissionsInterface;
1315

1416
/**
1517
* Interface FilesystemInterface
@@ -26,7 +28,9 @@ interface FilesystemInterface extends
2628
GetModificationTimestampInterface,
2729
GetStatusChangeTimestampInterface,
2830
SetAccessTimestampInterface,
29-
SetModificationTimestampInterface
31+
SetModificationTimestampInterface,
32+
GetPermissionsInterface,
33+
SetPermissionsInterface
3034
{
3135

3236
}

src/System/Util/Permissions.php

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,27 +14,26 @@
1414
*/
1515
class Permissions implements PermissionsInterface
1616
{
17+
protected const int CLASS_MASK = 0b111;
18+
protected const int READ_SHIFT = 6;
19+
protected const int WRITE_SHIFT = 3;
20+
protected const int EXECUTE_SHIFT = 0;
21+
1722
/**
1823
* @param int $permissions
1924
* @return static
2025
*/
2126
public static function fromNumeric(int $permissions): static
2227
{
2328
return new static(
24-
new PermissionsClass(
25-
($permissions & 0o100) === 0o100,
26-
($permissions & 0o200) === 0o200,
27-
($permissions & 0o400) === 0o400
29+
PermissionsClass::fromNumeric(
30+
($permissions >> static::READ_SHIFT) & static::CLASS_MASK
2831
),
29-
new PermissionsClass(
30-
($permissions & 0o010) === 0o010,
31-
($permissions & 0o020) === 0o020,
32-
($permissions & 0o040) === 0o040
32+
PermissionsClass::fromNumeric(
33+
($permissions >> static::WRITE_SHIFT) & static::CLASS_MASK
3334
),
34-
new PermissionsClass(
35-
($permissions & 0o001) === 0o001,
36-
($permissions & 0o002) === 0o002,
37-
($permissions & 0o004) === 0o004
35+
PermissionsClass::fromNumeric(
36+
($permissions >> static::EXECUTE_SHIFT) & static::CLASS_MASK
3837
)
3938
);
4039
}
@@ -103,4 +102,14 @@ public function setOther(PermissionsClassInterface $other): static
103102
$this->other = $other;
104103
return $this;
105104
}
105+
106+
/**
107+
* @inheritDoc
108+
*/
109+
public function toNumeric(): int
110+
{
111+
return ($this->user->toNumeric() << static::READ_SHIFT)
112+
+ ($this->group->toNumeric() << static::WRITE_SHIFT)
113+
+ ($this->other->toNumeric() << static::EXECUTE_SHIFT);
114+
}
106115
}

src/System/Util/PermissionsClass.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,23 @@
1313
*/
1414
class PermissionsClass implements PermissionsClassInterface
1515
{
16+
protected const int READ_MASK = 0b100;
17+
protected const int WRITE_MASK = 0b010;
18+
protected const int EXECUTE_MASK = 0b001;
19+
20+
/**
21+
* @param int $permissions
22+
* @return static
23+
*/
24+
public static function fromNumeric(int $permissions): static
25+
{
26+
return new static(
27+
($permissions & static::READ_MASK) === static::READ_MASK,
28+
($permissions & static::WRITE_MASK) === static::WRITE_MASK,
29+
($permissions & static::EXECUTE_MASK) === static::EXECUTE_MASK
30+
);
31+
}
32+
1633
/**
1734
* @param bool $read
1835
* @param bool $write
@@ -76,4 +93,14 @@ public function setExecute(bool $execute): static
7693
$this->execute = $execute;
7794
return $this;
7895
}
96+
97+
/**
98+
* @inheritDoc
99+
*/
100+
public function toNumeric(): int
101+
{
102+
return ($this->read ? static::READ_MASK : 0)
103+
+ ($this->write ? static::WRITE_MASK : 0)
104+
+ ($this->execute ? static::EXECUTE_MASK : 0);
105+
}
79106
}

test/Unit/System/FilesystemTestCase.php

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Aternos\IO\Test\Unit\System;
44

5+
use Aternos\IO\Exception\ChmodException;
56
use Aternos\IO\Exception\IOException;
67
use Aternos\IO\Exception\MoveException;
78
use Aternos\IO\Exception\PathOutsideElementException;
@@ -12,6 +13,7 @@
1213
use Aternos\IO\System\Directory\Directory;
1314
use Aternos\IO\System\FilesystemElement;
1415
use Aternos\IO\System\FilesystemInterface;
16+
use Aternos\IO\System\Util\Permissions;
1517

1618
abstract class FilesystemTestCase extends TmpDirTestCase
1719
{
@@ -426,6 +428,65 @@ public function testSetModificationTimestampThrowsExceptionOnImpossibleSet(): vo
426428
$element->setModificationTimestamp(1234567890);
427429
}
428430

431+
/**
432+
* @return void
433+
* @throws IOException
434+
* @throws StatException
435+
*/
436+
public function testGetPermissions(): void
437+
{
438+
$path = $this->getTmpPath() . "/test";
439+
$element = $this->createElement($path);
440+
$this->create($element);
441+
chmod($path, 0o755);
442+
$this->assertEquals(0o755, $element->getPermissions()->toNumeric());
443+
}
444+
445+
/**
446+
* @return void
447+
* @throws IOException
448+
* @throws StatException
449+
*/
450+
public function testThrowsExceptionOnGetPermissions(): void
451+
{
452+
$path = $this->getTmpPath() . "/test";
453+
$element = $this->createElement($path);
454+
$this->expectException(StatException::class);
455+
/** @noinspection SpellCheckingInspection */
456+
$this->expectExceptionMessage("Could not get permissions (" . $path . ")");
457+
$element->getPermissions();
458+
}
459+
460+
/**
461+
* @return void
462+
* @throws IOException
463+
* @throws ChmodException
464+
*/
465+
public function testSetPermissions(): void
466+
{
467+
$path = $this->getTmpPath() . "/test";
468+
$element = $this->createElement($path);
469+
$this->create($element);
470+
chmod($path, 0o777);
471+
$element->setPermissions(Permissions::fromNumeric(0o755));
472+
$this->assertEquals(0o755, fileperms($path) & 0o777);
473+
}
474+
475+
/**
476+
* @return void
477+
* @throws IOException
478+
* @throws ChmodException
479+
*/
480+
public function testThrowsExceptionOnSetPermissions(): void
481+
{
482+
$path = $this->getTmpPath() . "/test";
483+
$element = $this->createElement($path);
484+
$this->expectException(ChmodException::class);
485+
/** @noinspection SpellCheckingInspection */
486+
$this->expectExceptionMessage("Could not set permissions (" . $path . ")");
487+
$element->setPermissions(Permissions::fromNumeric(0o755));
488+
}
489+
429490
/**
430491
* @return void
431492
*/

0 commit comments

Comments
 (0)