Overview

Namespaces

  • Alchemy
    • core
      • query
      • schema
    • dialect
    • engine
    • orm
    • tests
    • util
      • promise
  • PHP

Classes

  • Promise
  • Signal
  • SignalFn_Then
  • Waitable

Interfaces

  • IPromisable

Exceptions

  • SerializableException
  • TimeoutException
  • TypeException
  • Overview
  • Namespace
  • Class
  • Tree
  1: <?php
  2: 
  3: namespace Alchemy\util\promise;
  4: 
  5: 
  6: /**
  7:  * Similar to a Signal, except it only allows a value to be resolved once.
  8:  * Encapsulates an unresolved value and a promise to resolve it later.
  9:  * Use it to simplify communication between asyncronous processes.
 10:  */
 11: class Promise extends Signal {
 12: 
 13:     protected $onResolvePromises = array();
 14: 
 15: 
 16:     /**
 17:      * Return a single promise that is resolved when the given array
 18:      * of promise are all resolved.
 19:      *
 20:      * @param array $promises
 21:      * @return Promise
 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:      * If possible, return a Promise to call the method later,
 52:      * else just call the method.
 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:      * Wait for the Promise to resolve and cast it to a string.
 71:      */
 72:     public function __tostring() {
 73:         return (string) $this->wait();
 74:     }
 75: 
 76: 
 77:     /**
 78:      * Get the return type of a given method on an IPromisable class, if known.
 79:      *
 80:      * @param  string $cls    class name
 81:      * @param  string $method method name
 82:      * @return string|null    return type
 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:      * Subclasses override this to do something during check()
105:      */
106:     protected function precheck() {
107:         if ($this->result === null) {
108:             parent::precheck();
109:         }
110:     }
111: 
112: 
113:     /**
114:      * A Promise will only resolve once. Further resolve()s will be ignored.
115:      *
116:      * @param  mixed $result
117:      * @return this
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:      * Returns a Promise of a callback on the results of this Promise.
136:      * If that in turn returns a Promise, it will wait on that Promise as well.
137:      * Use this to chain asyncronous callbacks.
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:  * This is a class instead of a lamda function so that it can potentially
154:  * be serialized, though that also requires that the callbacks and parent
155:  * Promises be serializable as well.
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: 
API documentation generated by ApiGen 2.8.0