Skip to content

Commit

Permalink
update the logic
Browse files Browse the repository at this point in the history
  • Loading branch information
yassinebenaid committed Feb 18, 2023
1 parent 563537d commit f3a23d3
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 78 deletions.
137 changes: 59 additions & 78 deletions src/Promise/Promise.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,113 +2,94 @@

namespace Eldia\Promise;


use Exception;

final class Promise
class Promise
{
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
use PromiseState,
PromiseCallbacks,
PromiseErrors;

private $state = self::PENDING;
private $result = null;
private $callbacks = [];
/** represent the recent promise's result */
protected mixed $result = null;


private function __construct(callable $executor)
/** make new promise */
public static function make(callable $executor): static
{
$resolve = function ($value) {
$this->fulfill($value);
};
$reject = function ($reason) {
$this->reject($reason);
};
try {
$executor($resolve, $reject);
} catch (Exception $e) {
$this->reject($e);
}
return new self($executor);
}

public static function make(callable $executer)

private function __construct(callable $executor)
{
return new self($executer);
$this->initState();

$this->try(
$executor,
$this->fulfill(...),
$this->reject(...)
);
}

public function then(callable $onFulfilled = null, callable $onRejected = null)

/** add fallbacks to promise */
public function then(callable $onSuccess, callable $onFailure = null)
{
$promise = new self(function ($resolve, $reject) use ($onFulfilled, $onRejected) {
$callback = function () use ($resolve, $reject, $onFulfilled, $onRejected) {
try {
if ($this->state == self::FULFILLED) {
if (is_callable($onFulfilled)) {
$result = $onFulfilled($this->result);
if ($result instanceof self) {
$result->then($resolve, $reject);
return;
}
$resolve($result);
} else {
$resolve($this->result);
}
} else if ($this->state == self::REJECTED) {
if (is_callable($onRejected)) {
$result = $onRejected($this->result);
if ($result instanceof self) {
$result->then($resolve, $reject);
return;
}
$resolve($result);
} else {
$reject($this->result);
}
}
} catch (Exception $e) {
$reject($e);
}
};
if ($this->state == self::PENDING) {
$this->callbacks[] = $callback;
} else {
$callback();
if ($this->hasError()) return $this;


return new self(function ($success, $failure) use ($onSuccess, $onFailure) {

if ($this->isFulfilled()) {
$success($this->try($onSuccess, $this->result));
} elseif ($this->isRejected() && $onFailure) {
$failure($this->try($onFailure, $this->result));
}
});
return $promise;
}


public function catch(callable $onRejected)
/** mark the promise as fulfilled */
private function fulfill($value = null)
{
return $this->then(null, $onRejected);
if ($this->isPending()) {
$this->fulfilled();
$this->result = $value;
}
}


private function fulfill($value)
/** mark the promise as rejected */
private function reject($value = null)
{
if ($this->state == self::PENDING) {
$this->state = self::FULFILLED;
if ($this->isPending()) {
$this->rejected();
$this->result = $value;
$this->callCallbacks();
}
}


private function reject($reason)
/** catch errors occured inside the promise */
public function catch(callable $callback)
{
if ($this->state == self::PENDING) {
$this->state = self::REJECTED;
$this->result = $reason;
$this->callCallbacks();
}
if (!$this->hasError()) return $this;

$exception_type = $this->getFirstParatemerType($callback);


if ($exception_type && !$this->error instanceof $exception_type) return $this;


$this->try($callback, $this->error);

return $this;
}


private function callCallbacks()
/** try to execute callback and catch errors */
private function try(callable $callback, ...$params)
{
foreach ($this->callbacks as $callback) {
$callback();
try {
return $this->call($callback, ...$params);
} catch (\Throwable $th) {
$this->error($th);
}
$this->callbacks = [];
}
}
48 changes: 48 additions & 0 deletions src/Promise/PromiseCallbacks.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace Eldia\Promise;

use ReflectionFunction;
use ReflectionMethod;

trait PromiseCallbacks
{

/** call a callable instance */
protected function call(callable $callback, ...$params)
{
return call_user_func($callback, ...$params);
}


/** get the first parameter type */
protected function getFirstParatemerType(callable $callback): mixed
{
return $this->getParatemerTypes($callback)[0];
}


/** get all the parameter types */
protected function getParatemerTypes(callable $callback): array
{
$reflector = $this->getReflector($callback);

$types = [];

foreach ($reflector->getParameters() as $parameter) {
$types[] = $parameter->getType()?->getName();
}


return $types;
}


/** get the reflector for the given callable */
protected function getReflector(callable $callback)
{
if (is_array($callback)) return new ReflectionMethod($callback[0], $callback[1]);

return new ReflectionFunction($callback);
}
}
24 changes: 24 additions & 0 deletions src/Promise/PromiseErrors.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Eldia\Promise;

use Throwable;

trait PromiseErrors
{
protected ?Throwable $error = null;


/** determine wether the promise has error or not */
protected function hasError(): bool
{
return !is_null($this->error);
}


/** add throwable error */
protected function error(Throwable $error)
{
$this->error = $error;
}
}
66 changes: 66 additions & 0 deletions src/Promise/PromiseState.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

namespace Eldia\Promise;

trait PromiseState
{
protected readonly string $PENDING;
protected readonly string $FULFILLED;
protected readonly string $REJECTED;

/**
* holds the current promise state
*
* @var string
*/
protected string $state;


/**
* initial stats values
*/
protected function initState(): void
{
$this->PENDING = 'pending';
$this->FULFILLED = 'fulfilled';
$this->REJECTED = 'rejected';

$this->pending();
}

/** determine wether the promise still pending */
protected function isPending(): bool
{
return $this->state === $this->PENDING;
}

/** determine wether the promise was fulfilled */
protected function isFulfilled(): bool
{
return $this->state === $this->FULFILLED;
}

/** determine wether the promise was rejected */
protected function isRejected(): bool
{
return $this->state === $this->REJECTED;
}

/** set the promise state to pending */
protected function pending(): void
{
$this->state = $this->PENDING;
}

/** set the promise state to fulfilled */
protected function fulfilled(): void
{
$this->state = $this->FULFILLED;
}

/** set the promise state to rejected */
protected function rejected(): void
{
$this->state = $this->REJECTED;
}
}

0 comments on commit f3a23d3

Please sign in to comment.