Skip to content

Commit

Permalink
Added more file permission functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
freost committed Oct 21, 2024
1 parent 1518665 commit 03253b7
Show file tree
Hide file tree
Showing 7 changed files with 357 additions and 3 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,12 @@ The major version bump is due to upping the required PHP version from `8.1` to `
* Added `Permission` enum with the following methods to make it easier to interact with file permissions:
- `Permission::calculate()`
- `Permission::hasPermissions()`
* Added `Permissions` class to that can interact with a set of `Permission` enum instances.
* Added `FileSystem::setPermissions()` method that accepts `Permission` enum values.
* Added `FileSystem::hasPermissions()` method.
* Added `FileSystem::getPermissions()` method that returns a `Permissions` instance.
* Added `FileInfo::hasPermissions()` method.
* Added `FileInfo::getPermissions()` method that returns a `Permissions` instance.

#### Changes

Expand Down
8 changes: 8 additions & 0 deletions src/mako/file/FileInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,12 @@ public function hasPermissions(Permission ...$permission): bool
{
return Permission::hasPermissions($this->getPerms() & Permission::FULL->value, ...$permission);
}

/**
* Returns the file permissions.
*/
public function getPermissions(): Permissions
{
return Permissions::fromInt($this->getPerms() & Permission::FULL->value);
}
}
8 changes: 8 additions & 0 deletions src/mako/file/FileSystem.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ public function hasPermission(string $path, Permission ...$permission): bool
return Permission::hasPermissions(fileperms($path) & Permission::FULL->value, ...$permission);
}

/**
* Returns the file permissions.
*/
public function getPermissions(string $path): Permissions
{
return Permissions::fromInt(fileperms($path) & Permission::FULL->value);
}

/**
* Returns TRUE if the file is readable and FALSE if not.
*/
Expand Down
6 changes: 4 additions & 2 deletions src/mako/file/Permission.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

use InvalidArgumentException;

use function vsprintf;

/**
* File permissions.
*/
Expand Down Expand Up @@ -63,10 +65,10 @@ public static function calculate(Permission ...$permission): int
public static function hasPermissions(int $permissions, Permission ...$permission): bool
{
if ($permissions < 0o000 || $permissions > 0o777) {
throw new InvalidArgumentException(vsprintf('The integer %s does not represent a valid octal between 0o000 and 0o777.', [$permissions]));
throw new InvalidArgumentException(vsprintf('The integer [ %s ] does not represent a valid octal between 0o000 and 0o777.', [$permissions]));
}

$permission = static::calculate(...$permission);
$permission = empty($permission) ? 0o000 : static::calculate(...$permission);

if ($permission === 0o000) {
return $permissions === 0o000;
Expand Down
128 changes: 128 additions & 0 deletions src/mako/file/Permissions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?php

/**
* @copyright Frederic G. Østby
* @license http://www.makoframework.com/license
*/

namespace mako\file;

use InvalidArgumentException;

use function decoct;
use function str_pad;
use function vsprintf;

/**
* File permission collection.
*/
class Permissions
{
/**
* Permissions.
*
* @var Permission[]
*/
protected array $permissions = [];

/**
* Constructor.
*/
public function __construct(Permission ...$permissions)
{
$this->permissions = empty($permissions) ? [Permission::NONE] : $permissions;
}

/**
* Creates a new permission collection from an integer.
*/
public static function fromInt(int $permissions): static
{
if ($permissions < Permission::NONE->value || $permissions > Permission::FULL->value) {
throw new InvalidArgumentException(vsprintf('The integer [ %s ] does not represent a valid octal between 0o000 and 0o777.', [$permissions]));
}

if ($permissions === Permission::NONE->value) {
return new static;

Check failure on line 46 in src/mako/file/Permissions.php

View workflow job for this annotation

GitHub Actions / Static analysis (8.3)

Unsafe usage of new static().
}

$permission = [];

foreach (Permission::cases() as $case) {
if ($case === Permission::NONE
|| $case === Permission::OWNER_FULL
|| $case === Permission::GROUP_FULL
|| $case === Permission::PUBLIC_FULL
|| $case === Permission::FULL) {
continue;
}

if (($permissions & $case->value) === $case->value) {
$permission[] = $case;
}
}

return new static(...$permission);

Check failure on line 65 in src/mako/file/Permissions.php

View workflow job for this annotation

GitHub Actions / Static analysis (8.3)

Unsafe usage of new static().
}

/**
* Returns the permissions.
*
* @return Permission[]
*/
public function getPermissions(): array
{
return $this->permissions;
}

/**
* Returns TRUE if the permissions contain the specified permissions and FALSE if not.
*/
public function hasPermissions(Permission ...$permissions): bool
{
return Permission::hasPermissions(Permission::calculate(...$this->permissions), ...$permissions);
}

/**
* Returns an integer representation of the permissions.
*/
public function toInt(): int
{
return Permission::calculate(...$this->permissions);
}

/**
* Returns an octal string representation of the permissions.
*/
public function toOctalString(): string
{
return str_pad(decoct(Permission::calculate(...$this->permissions)), 3, '0', STR_PAD_LEFT);
}

/**
* Returns a rwx string representation of a permission group.
*/
protected function getGroupAsRwxString(int $permissions, Permission $read, Permission $write, Permission $execute): string
{
$rwx = '';
$rwx .= ($permissions & $read->value) ? 'r' : '-';
$rwx .= ($permissions & $write->value) ? 'w' : '-';
$rwx .= ($permissions & $execute->value) ? 'x' : '-';

return $rwx;
}

/**
* Returns a rwx string representation of the permissions.
*/
public function toRwxString(): string
{
$permissions = Permission::calculate(...$this->permissions);

$owner = $this->getGroupAsRwxString($permissions, Permission::OWNER_READ, Permission::OWNER_WRITE, Permission::OWNER_EXECUTE);
$group = $this->getGroupAsRwxString($permissions, Permission::GROUP_READ, Permission::GROUP_WRITE, Permission::GROUP_EXECUTE);
$public = $this->getGroupAsRwxString($permissions, Permission::PUBLIC_READ, Permission::PUBLIC_WRITE, Permission::PUBLIC_EXECUTE);

return "{$owner}{$group}{$public}";
}
}
6 changes: 5 additions & 1 deletion tests/unit/file/PermissionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public function testCalculate(): void
{
// Test individual permissions

$this->assertSame(0o000, Permission::calculate());

$this->assertSame(0o000, Permission::calculate(Permission::NONE));

$this->assertSame(0o400, Permission::calculate(Permission::OWNER_READ));
Expand Down Expand Up @@ -79,7 +81,7 @@ public function testHasPermissionsWithInvalidPermissions(): void
{
$this->expectException(InvalidArgumentException::class);

$this->expectExceptionMessage('The integer 1337 does not represent a valid octal between 0o000 and 0o777.');
$this->expectExceptionMessage('The integer [ 1337 ] does not represent a valid octal between 0o000 and 0o777.');

Permission::hasPermissions(1337, Permission::NONE);
}
Expand Down Expand Up @@ -107,6 +109,8 @@ public function testHasPermissions(): void
*/
public function testHasPermissionsWithNoPermissions(): void
{
$this->assertTrue(Permission::hasPermissions(0o000));

$this->assertTrue(Permission::hasPermissions(0o000, Permission::NONE));

$this->assertFalse(Permission::hasPermissions(0o777, Permission::NONE));
Expand Down
Loading

0 comments on commit 03253b7

Please sign in to comment.