Skip to content

Commit

Permalink
Bumps once to v3 (#646)
Browse files Browse the repository at this point in the history
Co-authored-by: Deeka Wong <[email protected]>
  • Loading branch information
huangdijia and huangdijia authored May 27, 2024
1 parent e8c1210 commit 2dac84d
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 193 deletions.
41 changes: 31 additions & 10 deletions src/Functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,40 @@ function retry($times, callable $callback, $sleepMilliseconds = 0, $when = null)
}

/**
* Ensures a callable is only called once, and returns the result on subsequent calls.
* @template T
*
* @template TReturnType
*
* @param callable(): TReturnType $callback
* @return TReturnType
* @param (callable(): T) $callback
* @return T
*/
function once(callable $callback)
function once(callable $callback): mixed
{
$onceable = Onceable::tryFromTrace(
debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 2),
$callback,
$trace = debug_backtrace(
DEBUG_BACKTRACE_PROVIDE_OBJECT,
2
);

return $onceable ? Once::instance()->value($onceable) : call_user_func($callback);
$backtrace = new Once\Backtrace($trace);

if ($backtrace->getFunctionName() === 'eval') {
return call_user_func($callback);
}

$object = $backtrace->getObject();
$hash = $backtrace->getHash();
$cache = Once\Cache::getInstance();

if (is_string($object)) {
$object = $cache;
}

if (! $cache->isEnabled()) {
return call_user_func($callback, $backtrace->getArguments());
}

if (! $cache->has($object, $hash)) {
$result = call_user_func($callback, $backtrace->getArguments());
$cache->set($object, $hash, $result);
}

return $cache->get($object, $hash);
}
98 changes: 0 additions & 98 deletions src/Once.php

This file was deleted.

74 changes: 74 additions & 0 deletions src/Once/Backtrace.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

declare(strict_types=1);
/**
* This file is part of friendsofhyperf/components.
*
* @link https://github.com/friendsofhyperf/components
* @document https://github.com/friendsofhyperf/components/blob/main/README.md
* @contact [email protected]
*/

namespace FriendsOfHyperf\Support\Once;

class Backtrace
{
protected array $trace;

protected array $zeroStack;

public function __construct(array $trace)
{
$this->trace = $trace[1];

$this->zeroStack = $trace[0];
}

public function getArguments(): array
{
return $this->trace['args'];
}

public function getFunctionName(): string
{
return $this->trace['function'];
}

public function getObjectName(): ?string
{
return $this->trace['class'] ?? null;
}

public function getObject(): mixed
{
if ($this->globalFunction()) {
return $this->zeroStack['file'];
}

return $this->staticCall() ? $this->trace['class'] : $this->trace['object'];
}

public function getHash(): string
{
$normalizedArguments = array_map(function ($argument) {
return is_object($argument) ? spl_object_hash($argument) : $argument;
}, $this->getArguments());

$prefix = $this->getObjectName() . $this->getFunctionName();
if (str_contains($prefix, '{closure')) {
$prefix = $this->zeroStack['line'];
}

return md5($prefix . serialize($normalizedArguments));
}

protected function staticCall(): bool
{
return $this->trace['type'] === '::';
}

protected function globalFunction(): bool
{
return ! isset($this->trace['type']);
}
}
97 changes: 97 additions & 0 deletions src/Once/Cache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

declare(strict_types=1);
/**
* This file is part of friendsofhyperf/components.
*
* @link https://github.com/friendsofhyperf/components
* @document https://github.com/friendsofhyperf/components/blob/main/README.md
* @contact [email protected]
*/

namespace FriendsOfHyperf\Support\Once;

use Countable;
use WeakMap;

class Cache implements Countable
{
public WeakMap $values;

/**
* The cache instance.
* @var static|null
*/
protected static $cache;

protected bool $enabled = true;

protected function __construct()
{
$this->values = new WeakMap();
}

public static function getInstance(): static
{
return static::$cache ??= new static();
}

public function has(object $object, string $backtraceHash): bool
{
if (! isset($this->values[$object])) {
return false;
}

return array_key_exists($backtraceHash, $this->values[$object]);
}

public function get($object, string $backtraceHash): mixed
{
return $this->values[$object][$backtraceHash];
}

public function set(object $object, string $backtraceHash, mixed $value): void
{
$cached = $this->values[$object] ?? [];

$cached[$backtraceHash] = $value;

$this->values[$object] = $cached;
}

public function forget(object $object): void
{
unset($this->values[$object]);
}

public function flush(): self
{
$this->values = new WeakMap();

return $this;
}

public function enable(): self
{
$this->enabled = true;

return $this;
}

public function disable(): self
{
$this->enabled = false;

return $this;
}

public function isEnabled(): bool
{
return $this->enabled;
}

public function count(): int
{
return count($this->values);
}
}
Loading

0 comments on commit 2dac84d

Please sign in to comment.