Skip to content

Commit

Permalink
Improve performance of Path class (#605)
Browse files Browse the repository at this point in the history
Improve performance of Path class
  • Loading branch information
stloyd authored Oct 17, 2023
1 parent 429b4ab commit 867de38
Showing 1 changed file with 30 additions and 39 deletions.
69 changes: 30 additions & 39 deletions src/core/etl/src/Flow/ETL/Filesystem/Path.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,17 @@
use Flow\Serializer\Serializable;

/**
* @implements Serializable<array{path: string, scheme: string, options: array<string, mixed>}>
* @implements Serializable<array{path: string, scheme: string, options: array<string, mixed>, extension: string|false}>
*/
final class Path implements Serializable
{
private string|false $extension;

private string $path;

private string $scheme;

/**
* @param string $uri
* @param array<string, mixed> $options
*
* @throws InvalidArgumentException
Expand All @@ -38,14 +39,12 @@ public function __construct(string $uri, private readonly array $options = [])
}

$path = \array_key_exists('scheme', $urlParts)
?\str_replace($urlParts['scheme'] . '://', '', $uri)
? \str_replace($urlParts['scheme'] . '://', '', $uri)
: $uri;

if (\array_key_exists('scheme', $urlParts)) {
$path = !\str_starts_with($path, DIRECTORY_SEPARATOR) ? (DIRECTORY_SEPARATOR . $path) : $path;
}

if (!\array_key_exists('scheme', $urlParts)) {
} else {
if (!\str_starts_with($path, DIRECTORY_SEPARATOR)) {
throw new InvalidArgumentException("Relative paths are not supported, consider using instead Path::realpath: {$uri}");
}
Expand All @@ -57,25 +56,22 @@ public function __construct(string $uri, private readonly array $options = [])

$this->path = $path;
$this->scheme = \array_key_exists('scheme', $urlParts) ? $urlParts['scheme'] : 'file';
$this->extension = \pathinfo($this->path)['extension'] ?? false;
}

/**
* Turn relative path into absolute paths even when path does not exists or it's glob pattern.
*
* @param string $path
* @param array<string, mixed> $options
*
* @throws InvalidArgumentException
* @throws RuntimeException
*
* @return Path
*/
public static function realpath(string $path, array $options = []) : self
{
// "" - empty path is current, local directory
if ('' === $path) {
/** @phpstan-ignore-next-line */
return new self(\getcwd(), $options);
return new self(\getcwd() ?: '', $options);
}

// "non_local://path/to/file.txt" - non local paths can't be relative
Expand All @@ -100,11 +96,11 @@ public static function realpath(string $path, array $options = []) : self

$userData = (array) \posix_getpwuid(\posix_getuid());

/** @psalm-suppress TypeDoesNotContainType */
if (!\array_key_exists('dir', $userData) || !\is_string($userData['dir'])) {
if (!\is_string($userData['dir'] ?? null)) {
throw new RuntimeException("Can't resolve homedir for user executing script");
}

/** @psalm-suppress PossiblyUndefinedArrayOffset */
$realPath = $userData['dir'] . DIRECTORY_SEPARATOR . \substr($realPath, 1);
}
}
Expand All @@ -116,9 +112,8 @@ public static function realpath(string $path, array $options = []) : self

/** @var array<string> $absoluteParts */
$absoluteParts = [];
$parts = \explode(DIRECTORY_SEPARATOR, $realPath);

foreach ($parts as $part) {
foreach (\explode(DIRECTORY_SEPARATOR, $realPath) as $part) {
if ($part === '.' || $part === '') {
continue;
}
Expand Down Expand Up @@ -146,6 +141,7 @@ public function __serialize() : array
'scheme' => $this->scheme,
'path' => $this->path,
'options' => $this->options,
'extension' => $this->extension,
];
}

Expand All @@ -154,6 +150,7 @@ public function __unserialize(array $data) : void
$this->path = $data['path'];
$this->scheme = $data['scheme'];
$this->options = $data['options'];
$this->extension = $data['extension'];
}

public function addPartitions(Partition $partition, Partition ...$partitions) : self
Expand All @@ -176,20 +173,18 @@ public function context() : ResourceContext

public function extension() : string|false
{
$pathInfo = \pathinfo($this->path);

return \array_key_exists('extension', $pathInfo) ? $pathInfo['extension'] : false;
return $this->extension;
}

public function isEqual(self $path) : bool
{
return $this->uri() === $path->uri()
&& $this->options() === $path->options();
&& $this->options === $path->options();
}

public function isLocal() : bool
{
return $this->scheme() === 'file';
return $this->scheme === 'file';
}

public function isPattern() : bool
Expand Down Expand Up @@ -225,7 +220,7 @@ public function parentDirectory() : self
}

$path = \pathinfo($this->path);
$dirname = (\array_key_exists('dirname', $path)) ? (\ltrim($path['dirname'], DIRECTORY_SEPARATOR)) : '';
$dirname = \array_key_exists('dirname', $path) ? \ltrim($path['dirname'], DIRECTORY_SEPARATOR) : '';

$dirname = $dirname === '' ? '/' : $dirname;

Expand Down Expand Up @@ -254,9 +249,7 @@ public function path() : string

public function randomize() : self
{
$path = \pathinfo($this->path);

$extension = (\array_key_exists('extension', $path)) ? ('.' . $path['extension']) : '';
$extension = false !== $this->extension ? '.' . $this->extension : '';

return new self(
(\rtrim($this->uri(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . \str_replace('.', '', \uniqid('', true)) . $extension),
Expand All @@ -271,18 +264,18 @@ public function scheme() : string

public function setExtension(string $extension) : self
{
if ($this->extension()) {
if ($this->extension() === $extension) {
if ($this->extension) {
if ($this->extension === $extension) {
return $this;
}

$pathinfo = \pathinfo($this->path);
$path = ($pathinfo['dirname'] ?? '') . DIRECTORY_SEPARATOR . $pathinfo['filename'] . '.' . $extension;

return new self($this->scheme() . '://' . \ltrim($path, DIRECTORY_SEPARATOR), $this->options());
return new self($this->scheme . '://' . \ltrim($path, DIRECTORY_SEPARATOR), $this->options);
}

return new self($this->uri() . '.' . $extension, $this->options());
return new self($this->uri() . '.' . $extension, $this->options);
}

public function startsWith(self $path) : bool
Expand Down Expand Up @@ -335,7 +328,7 @@ private function fnmatch(string $pattern, string $filename, int $flags = 0) : bo
}

if ($flags & 4) {
if (($filename[0] == '.') && ($pattern[0] != '.')) {
if (($filename[0] === '.') && ($pattern[0] !== '.')) {
return false;
}
}
Expand Down Expand Up @@ -374,18 +367,16 @@ private function fnmatch(string $pattern, string $filename, int $flags = 0) : bo

private function isPathPattern(string $path) : bool
{
foreach (\array_filter(\explode(DIRECTORY_SEPARATOR, $path)) as $folder) {
if (\str_contains($folder, '*') || \str_contains($folder, '?')) {
return true;
}
if (\str_contains($path, '*') || \str_contains($path, '?')) {
return true;
}

if (\str_contains($folder, '[') && \str_contains($folder, ']')) {
return true;
}
if (\str_contains($path, '[') && \str_contains($path, ']')) {
return true;
}

if (\str_contains($folder, '{') && \str_contains($folder, '}')) {
return true;
}
if (\str_contains($path, '{') && \str_contains($path, '}')) {
return true;
}

return false;
Expand Down

0 comments on commit 867de38

Please sign in to comment.