1: <?php
2:
3: namespace Alchemy\util\promise;
4:
5:
6: 7: 8: 9: 10:
11: class Promise extends Signal {
12:
13: protected $onResolvePromises = array();
14:
15:
16: 17: 18: 19: 20: 21: 22:
23: public static function when(array $promises = array()) {
24: $results = array();
25: $when = new Promise();
26:
27: $wait = function() use (&$promises, &$results, &$when, &$wait) {
28: if (count($promises) == 0) {
29: $when->resolve($results);
30: return;
31: }
32:
33: $p = array_pop($promises);
34: $p->then(function($r) use (&$results, &$wait) {
35: $results[] = $r;
36: $wait();
37: });
38: };
39:
40: $wait();
41: return $when;
42: }
43:
44:
45: public function __get($name) {
46: return $this->__call('__get', array($name));
47: }
48:
49:
50: 51: 52: 53:
54: public function __call($name, $args) {
55: $type = self::get_return_type($this->type(), $name);
56:
57: if ($type) {
58: return $this->then(function($obj) use ($name, $args) {
59: return call_user_func_array(array($obj, $name), $args);
60: }, null, $type, false);
61: }
62:
63: return $name == '__get'
64: ? $this->expect()->{$args[0]}
65: : call_user_func_array(array($this->expect(), $name), $args);
66: }
67:
68:
69: 70: 71:
72: public function __tostring() {
73: return (string) $this->wait();
74: }
75:
76:
77: 78: 79: 80: 81: 82: 83:
84: public static function get_return_type($cls, $method) {
85: static $return_types = array();
86:
87: if (isset($return_types[$cls][$method])) {
88: return $return_types[$cls][$method];
89: }
90:
91: if (isset($return_types[$cls]) || !$cls || !$method) {
92: return false;
93: }
94:
95: $return_types[$cls] = method_exists($cls, "list_promisable_methods")
96: ? $cls::list_promisable_methods()
97: : false;
98:
99: return isset($return_types[$cls][$method]) ? $return_types[$cls][$method] : false;
100: }
101:
102:
103: 104: 105:
106: protected function precheck() {
107: if ($this->result === null) {
108: parent::precheck();
109: }
110: }
111:
112:
113: 114: 115: 116: 117: 118:
119: public function resolve($result) {
120: if ($this->result === null) {
121: parent::resolve($result);
122:
123: if ($this->result !== null) {
124: foreach($this->onResolvePromises as $promise) {
125: $promise->check();
126: }
127: }
128: }
129:
130: return $this;
131: }
132:
133:
134: 135: 136: 137: 138:
139: public function then($fnThen = null, $fnFail = null, $type = null, $check = true) {
140: $promise = new static(new SignalFn_Then($this, $fnThen, $fnFail), $type);
141:
142: if (is_null($this->result)) {
143: $this->onResolvePromises[] = $promise;
144: } elseif ($check) {
145: $promise->check();
146: }
147:
148: return $promise;
149: }
150: }
151:
152: 153: 154: 155: 156:
157: class SignalFn_Then {
158: protected $fnSource;
159: protected $fnThen;
160: protected $fnFail;
161:
162: public function __construct($fnSource, $fnThen = null, $fnFail = null) {
163: $this->fnSource = $fnSource;
164: $this->fnThen = $fnThen;
165: $this->fnFail = $fnFail;
166: }
167:
168: public function __invoke() {
169: $result = call_user_func($this->fnSource);
170:
171: if ($result === null) {
172: return null;
173: }
174:
175: $fnNext = ($result instanceof \Exception) ? $this->fnFail : $this->fnThen;
176: return $fnNext ? $fnNext($result) : $result;
177: }
178: }
179: