/** * @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; }
/** * @test */ public function it_returns_a_noop_disposable_if_primary_is_already_disposed() { $called = 0; $d = new CallbackDisposable(function () use(&$called) { $called++; }); $r = new RefCountDisposable($d); $r->dispose(); $this->assertEquals(1, $called); $d1 = $r->getDisposable(); $d1->dispose(); $r->dispose(); $this->assertEquals(1, $called); }