Esempio n. 1
0
 /**
  * @param \Rx\ObservableInterface $observable
  * @param \Rx\ObserverInterface $observer
  * @param \Rx\SchedulerInterface $scheduler
  * @return \Rx\DisposableInterface
  */
 public function __invoke(ObservableInterface $observable, ObserverInterface $observer, SchedulerInterface $scheduler = null)
 {
     $isDisposed = false;
     $disposable = new CompositeDisposable();
     $onError = function (\Exception $e) use(&$isDisposed, $observer, $observable, $scheduler, $disposable) {
         if ($isDisposed) {
             return;
         }
         try {
             /** @var Observable $result */
             $result = call_user_func($this->errorSelector, $e, $observable);
             $subscription = $result->subscribe($observer, $scheduler);
             $disposable->add($subscription);
         } catch (\Exception $e) {
             $observer->onError($e);
         }
     };
     $callbackObserver = new CallbackObserver([$observer, "onNext"], $onError, [$observer, "onCompleted"]);
     $subscription = $observable->subscribe($callbackObserver, $scheduler);
     $disposable->add($subscription);
     $disposable->add(new CallbackDisposable(function () use(&$isDisposed) {
         $isDisposed = true;
     }));
     return $disposable;
 }
Esempio n. 2
0
 public function __invoke(ObservableInterface $observable, ObserverInterface $observer, SchedulerInterface $scheduler = null)
 {
     $singleDisposable = new SingleAssignmentDisposable();
     $disposable = new CompositeDisposable();
     $disposable->add($singleDisposable);
     $callbackObserver = new CallbackObserver(function (Observable $x) use($disposable, $scheduler, $observer) {
         if ($this->hasCurrent) {
             return;
         }
         $this->hasCurrent = true;
         $inner = new SingleAssignmentDisposable();
         $disposable->add($inner);
         $innerSub = $x->subscribe(new CallbackObserver([$observer, "onNext"], [$observer, "onError"], function () use($disposable, $inner, $observer) {
             $disposable->remove($inner);
             $this->hasCurrent = false;
             if ($this->isStopped && $disposable->count() === 1) {
                 $observer->onCompleted();
             }
         }), $scheduler);
         $inner->setDisposable($innerSub);
     }, [$observer, 'onError'], function () use($disposable, $observer) {
         $this->isStopped = true;
         if (!$this->hasCurrent && $disposable->count() === 1) {
             $observer->onCompleted();
         }
     });
     $singleDisposable->setDisposable($observable->subscribe($callbackObserver, $scheduler));
     return $disposable;
 }
Esempio n. 3
0
 public function scheduleRecursive($action)
 {
     if (!is_callable($action)) {
         throw new InvalidArgumentException("Action should be a callable.");
     }
     $group = new CompositeDisposable();
     $scheduler = $this;
     $recursiveAction = null;
     $recursiveAction = function () use($action, &$scheduler, &$group, &$recursiveAction) {
         $action(function () use(&$scheduler, &$group, &$recursiveAction) {
             $isAdded = false;
             $isDone = true;
             $d = $scheduler->schedule(function () use(&$isAdded, &$isDone, &$group, &$recursiveAction, &$d) {
                 if (is_callable($recursiveAction)) {
                     $recursiveAction();
                 } else {
                     throw new \Exception("recursiveAction is not callable");
                 }
                 if ($isAdded) {
                     $group->remove($d);
                 } else {
                     $isDone = true;
                 }
             });
             if (!$isDone) {
                 $group->add($d);
                 $isAdded = true;
             }
         });
     };
     $group->add($this->schedule($recursiveAction));
     return $group;
 }
Esempio n. 4
0
 /**
  * @inheritDoc
  */
 public function __invoke(ObservableInterface $observable, ObserverInterface $observer, SchedulerInterface $scheduler = null)
 {
     if ($this->scheduler !== null) {
         $scheduler = $this->scheduler;
     }
     if ($scheduler === null) {
         throw new \Exception("You must use a scheduler that support non-zero delay.");
     }
     $lastScheduledTime = 0;
     $disposable = new CompositeDisposable();
     $sourceDisposable = new EmptyDisposable();
     $sourceDisposable = $observable->subscribe(new CallbackObserver(function ($x) use($scheduler, $observer, &$lastScheduledTime, $disposable) {
         $schedDisp = $scheduler->schedule(function () use($x, $observer, &$schedDisp, $disposable) {
             $observer->onNext($x);
             $disposable->remove($schedDisp);
         }, $this->delay);
         $disposable->add($schedDisp);
     }, function ($err) use($scheduler, $observer, &$lastScheduledTime, $disposable, &$sourceDisposable) {
         $disposable->remove($sourceDisposable);
         $sourceDisposable->dispose();
         $observer->onError($err);
     }, function () use($scheduler, $observer, $disposable, &$sourceDisposable) {
         $disposable->remove($sourceDisposable);
         $sourceDisposable->dispose();
         $schedDisp = $scheduler->schedule(function () use($observer, &$schedDisp, $disposable) {
             $observer->onCompleted();
             $disposable->remove($schedDisp);
         }, $this->delay);
         $disposable->add($schedDisp);
     }), $scheduler);
     $disposable->add($sourceDisposable);
     return $disposable;
 }
Esempio n. 5
0
 /**
  * @param \Rx\ObservableInterface $observable
  * @param \Rx\ObserverInterface $observer
  * @param \Rx\SchedulerInterface $scheduler
  * @return \Rx\DisposableInterface
  */
 public function __invoke(ObservableInterface $observable, ObserverInterface $observer, SchedulerInterface $scheduler = null)
 {
     $group = new CompositeDisposable();
     $isStopped = false;
     $sourceSubscription = new SingleAssignmentDisposable();
     $group->add($sourceSubscription);
     $callbackObserver = new CallbackObserver(function (ObservableInterface $innerSource) use(&$group, &$isStopped, $observer, &$scheduler) {
         $innerSubscription = new SingleAssignmentDisposable();
         $group->add($innerSubscription);
         $innerSubscription->setDisposable($innerSource->subscribe(new CallbackObserver(function ($nextValue) use($observer) {
             $observer->onNext($nextValue);
         }, function ($error) use($observer) {
             $observer->onError($error);
         }, function () use(&$group, &$innerSubscription, &$isStopped, $observer) {
             $group->remove($innerSubscription);
             if ($isStopped && $group->count() === 1) {
                 $observer->onCompleted();
             }
         }), $scheduler));
     }, [$observer, 'onError'], function () use(&$group, &$isStopped, $observer) {
         $isStopped = true;
         if ($group->count() === 1) {
             $observer->onCompleted();
         }
     });
     $subscription = $this->sources->subscribe($callbackObserver, $scheduler);
     $sourceSubscription->setDisposable($subscription);
     return $group;
 }
Esempio n. 6
0
 public function subscribe(ObserverInterface $observer, SchedulerInterface $scheduler = null)
 {
     $value = $this->value;
     $scheduler = $scheduler ?: new ImmediateScheduler();
     $disposable = new CompositeDisposable();
     $disposable->add($scheduler->schedule(function () use($observer, $value) {
         $observer->onNext($value);
     }));
     $disposable->add($scheduler->schedule(function () use($observer) {
         $observer->onCompleted();
     }));
     return $disposable;
 }
Esempio n. 7
0
 public function subscribe(ObserverInterface $observer)
 {
     $this->subscriptions[] = new Subscription($this->scheduler->getClock());
     $index = count($this->subscriptions) - 1;
     $currentObservable = $this;
     $disposable = new CompositeDisposable();
     $scheduler = $this->scheduler;
     $isDisposed = false;
     foreach ($this->messages as $message) {
         $notification = $message->getValue();
         $time = $message->getTime();
         $schedule = function ($innerNotification) use(&$disposable, &$currentObservable, $observer, $scheduler, $time, &$isDisposed) {
             $disposable->add($scheduler->scheduleRelativeWithState(null, $time, function () use($observer, $innerNotification, &$isDisposed) {
                 if (!$isDisposed) {
                     $innerNotification->accept($observer);
                 }
                 return new EmptyDisposable();
             }));
         };
         $schedule($notification);
     }
     $subscriptions =& $this->subscriptions;
     return new CallbackDisposable(function () use(&$currentObservable, $index, $observer, $scheduler, &$subscriptions, &$isDisposed) {
         $isDisposed = true;
         $subscriptions[$index] = new Subscription($subscriptions[$index]->getSubscribed(), $scheduler->getClock());
     });
 }
Esempio n. 8
0
 /**
  * @inheritDoc
  */
 public function __invoke(ObservableInterface $observable, ObserverInterface $observer, SchedulerInterface $scheduler = null)
 {
     $callbackObserver = new CallbackObserver(function (Observable $innerObservable) {
         $this->observables[] = $innerObservable;
     }, [$observer, 'onError'], function () use($observer, $scheduler) {
         if (count($this->observables) === 0) {
             $observer->onCompleted();
             return;
         }
         foreach ($this->observables as $i => $innerObs) {
             $subscription = $this->subscribeToResult($innerObs, $scheduler, $observer, $i);
             $this->subscriptions[] = $subscription;
             $this->innerSubscription->add($subscription);
         }
         $this->observables = null;
     });
     $subscription = $observable->subscribe($callbackObserver, $scheduler);
     return new BinaryDisposable($subscription, $this->innerSubscription);
 }
Esempio n. 9
0
 /**
  * @param ObservableInterface $observable
  * @param ObserverInterface $observer
  * @param SchedulerInterface|null $scheduler
  * @return CompositeDisposable
  */
 public function __invoke(ObservableInterface $observable, ObserverInterface $observer, SchedulerInterface $scheduler = null)
 {
     $subscription = $observable->subscribe(new CallbackObserver(function (ObservableInterface $innerObservable) use($observable, $observer, $scheduler) {
         try {
             if ($this->startBuffering === true) {
                 $this->buffer[] = $innerObservable;
                 return;
             }
             $onCompleted = function () use(&$subscribeToInner, $observer) {
                 $this->disposable->remove($this->innerDisposable);
                 $this->innerDisposable->dispose();
                 $this->innerCompleted = true;
                 $obs = array_shift($this->buffer);
                 if (empty($this->buffer)) {
                     $this->startBuffering = false;
                 }
                 if ($obs) {
                     $subscribeToInner($obs);
                 } elseif ($this->sourceCompleted === true) {
                     $observer->onCompleted();
                 }
             };
             $subscribeToInner = function ($observable) use($observer, $scheduler, &$onCompleted) {
                 $callbackObserver = new CallbackObserver([$observer, 'onNext'], [$observer, 'onError'], $onCompleted);
                 $this->innerCompleted = false;
                 $this->startBuffering = true;
                 $this->innerDisposable = $observable->subscribe($callbackObserver, $scheduler);
                 $this->disposable->add($this->innerDisposable);
             };
             $subscribeToInner($innerObservable);
         } catch (\Exception $e) {
             $observer->onError($e);
         }
     }, [$observer, 'onError'], function () use($observer) {
         $this->sourceCompleted = true;
         if ($this->innerCompleted === true) {
             $observer->onCompleted();
         }
     }), $scheduler);
     $this->disposable->add($subscription);
     return $this->disposable;
 }
Esempio n. 10
0
 /**
  * @inheritDoc
  */
 public function __invoke(ObservableInterface $observable, ObserverInterface $observer, SchedulerInterface $scheduler = null)
 {
     $errors = new Subject();
     $disposable = new CompositeDisposable();
     $sourceDisposable = new EmptyDisposable();
     $innerCompleted = false;
     $sourceError = false;
     try {
         $when = call_user_func($this->notificationHandler, $errors->asObservable());
     } catch (\Exception $e) {
         $observer->onError($e);
         return;
     }
     $subscribeToSource = function () use($observer, $disposable, $observable, &$sourceError, $errors, &$sourceDisposable, $scheduler, &$innerCompleted) {
         $sourceError = false;
         $sourceDisposable = $observable->subscribe(new CallbackObserver([$observer, 'onNext'], function ($err) use(&$sourceError, $errors, $disposable, &$sourceDisposable, &$innerCompleted, $observer) {
             $sourceError = true;
             $disposable->remove($sourceDisposable);
             $sourceDisposable->dispose();
             if ($innerCompleted) {
                 $observer->onCompleted();
                 return;
             }
             $errors->onNext($err);
         }, [$observer, 'onCompleted']), $scheduler);
         $disposable->add($sourceDisposable);
     };
     $whenDisposable = $when->subscribe(new CallbackObserver(function ($x) use($subscribeToSource, &$sourceError) {
         if ($sourceError) {
             $sourceError = false;
             $subscribeToSource();
         }
     }, [$observer, 'onError'], function () use(&$innerCompleted, &$sourceError, $observer) {
         $innerCompleted = true;
         if ($sourceError) {
             $observer->onCompleted();
         }
     }), $scheduler);
     $disposable->add($whenDisposable);
     $subscribeToSource();
     return $disposable;
 }
Esempio n. 11
0
 public function scheduleRecursive(callable $action)
 {
     if (!is_callable($action)) {
         throw new InvalidArgumentException("Action should be a callable.");
     }
     $goAgain = true;
     $disposable = new CompositeDisposable();
     $recursiveAction = function () use($action, &$goAgain, $disposable) {
         while ($goAgain) {
             $goAgain = false;
             $disposable->add($this->schedule(function () use($action, &$goAgain, $disposable) {
                 return $action(function () use(&$goAgain, $action) {
                     $goAgain = true;
                 });
             }));
         }
     };
     $disposable->add($this->schedule($recursiveAction));
     return $disposable;
 }
Esempio n. 12
0
 /**
  * @param \Rx\ObservableInterface $observable
  * @param \Rx\ObserverInterface $observer
  * @param \Rx\SchedulerInterface $scheduler
  * @return \Rx\DisposableInterface
  */
 public function __invoke(ObservableInterface $observable, ObserverInterface $observer, SchedulerInterface $scheduler = null)
 {
     $disposed = false;
     $disposable = new CompositeDisposable();
     $selectObserver = new CallbackObserver(function ($nextValue) use($observer, &$disposed) {
         $value = null;
         try {
             $value = call_user_func_array($this->selector, [$nextValue]);
         } catch (\Exception $e) {
             $observer->onError($e);
         }
         if (!$disposed) {
             $observer->onNext($value);
         }
     }, [$observer, 'onError'], [$observer, 'onCompleted']);
     $disposable->add(new CallbackDisposable(function () use(&$disposed) {
         $disposed = true;
     }));
     $disposable->add($observable->subscribe($selectObserver, $scheduler));
     return $disposable;
 }
Esempio n. 13
0
 /**
  * @inheritDoc
  */
 public function __invoke(ObservableInterface $observable, ObserverInterface $observer, SchedulerInterface $scheduler = null)
 {
     $outerDisposable = new SerialDisposable();
     $this->disposable->add($outerDisposable);
     $subscribe = function () use($outerDisposable, $observable, $observer, $scheduler, &$subscribe) {
         $this->sourceComplete = false;
         $outerSubscription = $observable->subscribe(new CallbackObserver([$observer, "onNext"], [$observer, "onError"], function () use($observer, &$subscribe, $outerDisposable) {
             $this->sourceComplete = true;
             if (!$this->repeat) {
                 $observer->onCompleted();
                 return;
             }
             $outerDisposable->setDisposable(new EmptyDisposable());
             $this->completions->onNext(++$this->count);
         }), $scheduler);
         $outerDisposable->setDisposable($outerSubscription);
     };
     $notifierDisposable = $this->notifier->subscribe(new CallbackObserver(function () use(&$subscribe, $scheduler) {
         $subscribe();
     }, function ($ex) use($observer) {
         $this->repeat = false;
         $observer->onError($ex);
     }, function () use($observer) {
         $this->repeat = false;
         if ($this->sourceComplete) {
             $observer->onCompleted();
         }
     }), $scheduler);
     $this->disposable->add($notifierDisposable);
     try {
         $handled = call_user_func($this->notificationHandler, $this->completions->asObservable());
         $handledDisposable = $handled->subscribe($this->notifier, $scheduler);
         $this->disposable->add($handledDisposable);
     } catch (\Exception $e) {
         $observer->onError($e);
         return new EmptyDisposable();
     }
     $subscribe();
     return $this->disposable;
 }
Esempio n. 14
0
 /**
  * @param \Rx\ObservableInterface $observable
  * @param \Rx\ObserverInterface $observer
  * @param \Rx\SchedulerInterface $scheduler
  * @return \Rx\DisposableInterface
  */
 public function __invoke(ObservableInterface $observable, ObserverInterface $observer, SchedulerInterface $scheduler = null)
 {
     array_unshift($this->observables, $observable);
     $observables =& $this->observables;
     $compositeDisposable = new CompositeDisposable();
     $hasValue = [];
     $values = array_keys($observables);
     $count = count($observables);
     $waitingToComplete = $count;
     $waitingForValues = $count;
     foreach ($observables as $key => $o) {
         $hasValue[$key] = false;
         $cbObserver = new CallbackObserver(function ($value) use($count, &$hasValue, $key, &$values, $observer, &$waitingForValues, &$waitingToComplete) {
             // If an observable has completed before it has emitted, we need to complete right away
             if ($waitingForValues > $waitingToComplete) {
                 $observer->onCompleted();
                 return;
             }
             if ($waitingForValues > 0 && !$hasValue[$key]) {
                 $hasValue[$key] = true;
                 $waitingForValues--;
             }
             $values[$key] = $value;
             if ($waitingForValues === 0) {
                 try {
                     $result = call_user_func_array($this->resultSelector, $values);
                     $observer->onNext($result);
                 } catch (\Exception $e) {
                     $observer->onError($e);
                 }
             }
         }, [$observer, 'onError'], function () use(&$waitingToComplete, $observer) {
             $waitingToComplete--;
             if ($waitingToComplete === 0) {
                 $observer->onCompleted();
             }
         });
         $subscription = $o->subscribe($cbObserver, $scheduler);
         $compositeDisposable->add($subscription);
     }
     return $compositeDisposable;
 }
Esempio n. 15
0
 /**
  * @inheritDoc
  */
 public function __invoke(ObservableInterface $observable, ObserverInterface $observer, SchedulerInterface $scheduler = null)
 {
     array_unshift($this->sources, $observable);
     $this->numberOfSources = count($this->sources);
     $disposable = new CompositeDisposable();
     $this->completesRemaining = $this->numberOfSources;
     for ($i = 0; $i < $this->numberOfSources; $i++) {
         $this->queues[$i] = new \SplQueue();
         $this->completed[$i] = false;
     }
     for ($i = 0; $i < $this->numberOfSources; $i++) {
         $source = $this->sources[$i];
         $cbObserver = new CallbackObserver(function ($x) use($i, $observer, $scheduler) {
             // if there is another item in the sequence after one of the other source
             // observables completes, we need to complete at this time to match the
             // behavior of RxJS
             if ($this->completesRemaining < $this->numberOfSources) {
                 // check for completed and empty queues
                 for ($j = 0; $j < $this->numberOfSources; $j++) {
                     if ($this->completed[$j] && count($this->queues[$j]) === 0) {
                         $observer->onCompleted();
                         return;
                     }
                 }
             }
             $this->queues[$i]->enqueue($x);
             for ($j = 0; $j < $this->numberOfSources; $j++) {
                 if ($this->queues[$j]->isEmpty()) {
                     return;
                 }
             }
             $params = [];
             for ($j = 0; $j < $this->numberOfSources; $j++) {
                 $params[] = $this->queues[$j]->dequeue();
             }
             try {
                 $observer->onNext(call_user_func_array($this->resultSelector, $params));
             } catch (\Exception $e) {
                 $observer->onError($e);
             }
         }, function ($e) use($observer) {
             $observer->onError($e);
         }, function () use($i, $observer) {
             $this->completesRemaining--;
             $this->completed[$i] = true;
             if ($this->completesRemaining === 0) {
                 $observer->onCompleted();
             }
         });
         $subscription = $source->subscribe($cbObserver, $scheduler);
         $disposable->add($subscription);
     }
     return $disposable;
 }
Esempio n. 16
0
 /**
  * @test
  */
 public function clear_disposes_all_contained_disposables_but_not_the_composite_disposable()
 {
     $disposed1 = false;
     $disposed2 = false;
     $d1 = new CallbackDisposable(function () use(&$disposed1) {
         $disposed1 = true;
     });
     $d2 = new CallbackDisposable(function () use(&$disposed2) {
         $disposed2 = true;
     });
     $disposable = new CompositeDisposable(array($d1, $d2));
     $disposable->clear();
     $this->assertTrue($disposed1);
     $this->assertTrue($disposed2);
     $disposed3 = false;
     $d3 = new CallbackDisposable(function () use(&$disposed3) {
         $disposed3 = true;
     });
     $disposable->add($d3);
     $this->assertFalse($disposed3);
 }
Esempio n. 17
0
 /**
  * @param \Rx\ObservableInterface $observable
  * @param \Rx\ObserverInterface $observer
  * @param \Rx\SchedulerInterface $scheduler
  * @return \Rx\DisposableInterface
  */
 public function __invoke(ObservableInterface $observable, ObserverInterface $observer, SchedulerInterface $scheduler = null)
 {
     $map = [];
     $groupDisposable = new CompositeDisposable();
     $refCountDisposable = new RefCountDisposable($groupDisposable);
     $keySelector = $this->keySelector;
     $elementSelector = $this->elementSelector;
     $durationSelector = $this->durationSelector;
     $keySerializer = $this->keySerializer;
     $sourceEmits = true;
     $callbackObserver = new CallbackObserver(function ($value) use(&$map, $keySelector, $elementSelector, $durationSelector, $observer, $keySerializer, $groupDisposable, $refCountDisposable, $scheduler, &$sourceEmits) {
         try {
             $key = $keySelector($value);
             $serializedKey = $keySerializer($key);
         } catch (Exception $e) {
             foreach ($map as $groupObserver) {
                 $groupObserver->onError($e);
             }
             $observer->onError($e);
             return;
         }
         $fireNewMapEntry = false;
         try {
             if (!isset($map[$serializedKey])) {
                 $map[$serializedKey] = new Subject();
                 $fireNewMapEntry = true;
             }
             $writer = $map[$serializedKey];
         } catch (Exception $e) {
             foreach ($map as $groupObserver) {
                 $groupObserver->onError($e);
             }
             $observer->onError($e);
             return;
         }
         if ($fireNewMapEntry) {
             $group = new GroupedObservable($key, $writer, $refCountDisposable);
             $durationGroup = new GroupedObservable($key, $writer);
             try {
                 $duration = $durationSelector($durationGroup);
             } catch (\Exception $e) {
                 foreach ($map as $groupObserver) {
                     $groupObserver->onError($e);
                 }
                 $observer->onError($e);
                 return;
             }
             if ($sourceEmits) {
                 $observer->onNext($group);
             }
             $md = new SingleAssignmentDisposable();
             $groupDisposable->add($md);
             $expire = function () use(&$map, &$md, $serializedKey, &$writer, &$groupDisposable) {
                 if (isset($map[$serializedKey])) {
                     unset($map[$serializedKey]);
                     $writer->onCompleted();
                 }
                 $groupDisposable->remove($md);
             };
             $callbackObserver = new CallbackObserver(function () {
             }, function (Exception $exception) use($map, $observer) {
                 foreach ($map as $writer) {
                     $writer->onError($exception);
                 }
                 $observer->onError($exception);
             }, function () use($expire) {
                 $expire();
             });
             $subscription = $duration->take(1)->subscribe($callbackObserver, $scheduler);
             $md->setDisposable($subscription);
         }
         try {
             $element = $elementSelector($value);
         } catch (Exception $exception) {
             foreach ($map as $writer) {
                 $writer->onError($exception);
             }
             $observer->onError($exception);
             return;
         }
         $writer->onNext($element);
     }, function (Exception $error) use(&$map, $observer) {
         foreach ($map as $writer) {
             $writer->onError($error);
         }
         $observer->onError($error);
     }, function () use(&$map, $observer) {
         foreach ($map as $writer) {
             $writer->onCompleted();
         }
         $observer->onCompleted();
     });
     $subscription = $observable->subscribe($callbackObserver, $scheduler);
     $groupDisposable->add($subscription);
     $sourceDisposable = new CallbackDisposable(function () use($refCountDisposable, &$sourceEmits) {
         $sourceEmits = false;
         $refCountDisposable->dispose();
     });
     return $sourceDisposable;
 }
Esempio n. 18
0
 public function groupByUntil($keySelector, $elementSelector = null, $durationSelector = null, $keySerializer = null)
 {
     $currentObservable = $this;
     if (!is_callable($keySelector)) {
         throw new InvalidArgumentException('Key selector should be a callable.');
     }
     if (null === $elementSelector) {
         $elementSelector = function ($elem) {
             return $elem;
         };
     } else {
         if (!is_callable($elementSelector)) {
             throw new InvalidArgumentException('Element selector should be a callable.');
         }
     }
     if (null === $durationSelector) {
         $durationSelector = function ($x) {
             return $x;
         };
     } else {
         if (!is_callable($durationSelector)) {
             throw new InvalidArgumentException('Duration selector should be a callable.');
         }
     }
     if (null === $keySerializer) {
         $keySerializer = function ($x) {
             return $x;
         };
     } else {
         if (!is_callable($keySerializer)) {
             throw new InvalidArgumentException('Key serializer should be a callable.');
         }
     }
     return new AnonymousObservable(function ($observer, $scheduler) use($currentObservable, $keySelector, $elementSelector, $durationSelector, $keySerializer) {
         $map = array();
         $groupDisposable = new CompositeDisposable();
         $refCountDisposable = new RefCountDisposable($groupDisposable);
         $groupDisposable->add($currentObservable->subscribeCallback(function ($value) use(&$map, $keySelector, $elementSelector, $durationSelector, $observer, $keySerializer, $groupDisposable, $refCountDisposable) {
             try {
                 $key = $keySelector($value);
                 $serializedKey = $keySerializer($key);
             } catch (Exception $e) {
                 foreach ($map as $groupObserver) {
                     $groupObserver->onError($e);
                 }
                 $observer->onError($e);
                 return;
             }
             $fireNewMapEntry = false;
             try {
                 if (!isset($map[$serializedKey])) {
                     $map[$serializedKey] = new Subject();
                     $fireNewMapEntry = true;
                 }
                 $writer = $map[$serializedKey];
             } catch (Exception $e) {
                 foreach ($map as $groupObserver) {
                     $groupObserver->onError($e);
                 }
                 $observer->onError($e);
                 return;
             }
             if ($fireNewMapEntry) {
                 $group = new GroupedObservable($key, $writer, $refCountDisposable);
                 $durationGroup = new GroupedObservable($key, $writer);
                 try {
                     $duration = $durationSelector($durationGroup);
                 } catch (Exception $e) {
                     foreach ($map as $groupObserver) {
                         $groupObserver->onError($e);
                     }
                     $observer->onError($e);
                     return;
                 }
                 $observer->onNext($group);
                 $md = new SingleAssignmentDisposable();
                 $groupDisposable->add($md);
                 $expire = function () use(&$map, &$md, $serializedKey, &$writer, &$groupDisposable) {
                     if (isset($map[$serializedKey])) {
                         unset($map[$serializedKey]);
                         $writer->onCompleted();
                     }
                     $groupDisposable->remove($md);
                 };
                 $md->setDisposable($duration->take(1)->subscribeCallback(function () {
                 }, function (Exception $exception) use($map, $observer) {
                     foreach ($map as $writer) {
                         $writer->onError($exception);
                     }
                     $observer->onError($exception);
                 }, function () use($expire) {
                     $expire();
                 }));
             }
             try {
                 $element = $elementSelector($value);
             } catch (Exception $exception) {
                 foreach ($map as $writer) {
                     $writer->onError($exception);
                 }
                 $observer->onError($exception);
                 return;
             }
             $writer->onNext($element);
         }, function (Exception $error) use(&$map, $observer) {
             foreach ($map as $writer) {
                 $writer->onError($error);
             }
             $observer->onError($error);
         }, function () use(&$map, $observer) {
             foreach ($map as $writer) {
                 $writer->onCompleted();
             }
             $observer->onCompleted();
         }, $scheduler));
         return $refCountDisposable;
     });
 }