Skip to content

Commit c9a3be3

Browse files
authored
Merge pull request #41 from clue-labs/collection
Merge Collector into ActionSender and Collection now extends Response
2 parents 8d0eaea + 9345baa commit c9a3be3

6 files changed

Lines changed: 165 additions & 116 deletions

File tree

README.md

Lines changed: 87 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ monitor the status of subscribers, channels or queues.
4747
* [getActionId()](#getactionid)
4848
* [Response](#response)
4949
* [getCommandOutput()](#getcommandoutput)
50+
* [Collection](#collection)
51+
* [getEntryEvents()](#getentryevents)
52+
* [getCompleteEvent()](#getcompleteevent)
5053
* [Action](#action)
5154
* [Event](#event)
5255
* [getName()](#getname)
@@ -205,9 +208,17 @@ $sender = new ActionSender($client);
205208
All public methods resemble their respective AMI actions.
206209

207210
```php
208-
$sender->ping()->then(function (Response $response) {
209-
// response received for ping action
210-
});
211+
$sender->login($name, $pass);
212+
$sender->logoff();
213+
$sender->ping();
214+
$sender->command($command);
215+
$sender->events($eventMask);
216+
217+
$sender->coreShowChannels();
218+
$sender->sipPeers();
219+
$sender->agents();
220+
221+
// many more…
211222
```
212223

213224
Listing all available actions is out of scope here, please refer to the [class outline](src/ActionSender.php).
@@ -237,6 +248,10 @@ $sender->ping()->then(
237248
});
238249
```
239250

251+
All actions resolve with a [`Response`](#response) object on success,
252+
some actions are documented to return the specialized [`Collection`](#collection)
253+
object to contain a list of entries.
254+
240255
#### Custom actions
241256

242257
Using the `ActionSender` is not strictly necessary, but is the recommended way to execute common actions.
@@ -247,7 +262,8 @@ A PR that updates the `ActionSender` is very much appreciated :)
247262

248263
### Message
249264

250-
The `Message` is an abstract base class for the [`Response`](#response), [`Action`](#action) and [`Event`](#event) value objects.
265+
The `Message` is an abstract base class for the [`Response`](#response),
266+
[`Action`](#action) and [`Event`](#event) value objects.
251267
It provides a common interface for these three message types.
252268

253269
Each `Message` consists of any number of fields with each having a name and one or multiple values.
@@ -272,12 +288,12 @@ The `getFields(): array` method can be used to get an array of all fields.
272288
The `getActionId(): string` method can be used to get the unique action ID of this message.
273289
This is a shortcut to get the value of the "ActionID" field.
274290

275-
#### Response
291+
### Response
276292

277293
The `Response` value object represents the incoming response received from the AMI.
278294
It shares all properties of the [`Message`](#message) parent class.
279295

280-
##### getCommandOutput()
296+
#### getCommandOutput()
281297

282298
The `getCommandOutput(): ?string` method can be used to get the resulting output of
283299
a "command" [`Action`](#action).
@@ -290,17 +306,79 @@ $sender->command('help')->then(function (Response $response) {
290306
});
291307
```
292308

293-
#### Action
309+
### Collection
310+
311+
The `Collection` value object represents an incoming response received from the AMI
312+
for certain actions that return a list of entries.
313+
It shares all properties of the [`Response`](#response) parent class.
314+
315+
You can access the `Collection` like a normal `Response` in order to access
316+
the leading `Response` for this collection or you can use the below methods
317+
to access the list entries and completion event.
318+
319+
```
320+
Action: CoreShowChannels
321+
322+
Response: Success
323+
EventList: start
324+
Message: Channels will follow
325+
326+
Event: CoreShowChannel
327+
Channel: SIP / 123
328+
ChannelState: 6
329+
ChannelStateDesc: Up
330+
331+
332+
Event: CoreShowChannel
333+
Channel: SIP / 456
334+
ChannelState: 6
335+
ChannelStateDesc: Up
336+
337+
338+
Event: CoreShowChannel
339+
Channel: SIP / 789
340+
ChannelState: 6
341+
ChannelStateDesc: Up
342+
343+
344+
Event: CoreShowChannelsComplete
345+
EventList: Complete
346+
ListItems: 3
347+
```
348+
349+
#### getEntryEvents()
350+
351+
The `getEntryEvents(): Event[]` method can be used to get the list of all
352+
intermediary `Event` objects where each entry represents a single entry in the
353+
collection.
354+
355+
```php
356+
foreach ($collection->getEntryEvents() as $entry) {
357+
/* @var $entry Event */
358+
echo $entry->getFieldValue('Channel') . PHP_EOL;
359+
}
360+
```
361+
362+
#### getCompleteEvent()
363+
364+
The `getCompleteEvent(): Event` method can be used to get the trailing
365+
`Event` that completes this collection.
366+
367+
```php
368+
echo $collection->getCompleteEvent()->getFieldValue('ListItems') . PHP_EOL;
369+
```
370+
371+
### Action
294372

295373
The `Action` value object represents an outgoing action message to be sent to the AMI.
296374
It shares all properties of the [`Message`](#message) parent class.
297375

298-
#### Event
376+
### Event
299377

300378
The `Event` value object represents the incoming event received from the AMI.
301379
It shares all properties of the [`Message`](#message) parent class.
302380

303-
##### getName()
381+
#### getName()
304382

305383
The `getName(): ?string` method can be used to get the name of the event.
306384
This is a shortcut to get the value of the "Event" field.

examples/peers.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
<?php
22

3-
use Clue\React\Ami\Factory;
4-
use Clue\React\Ami\Client;
53
use Clue\React\Ami\ActionSender;
6-
use Clue\React\Ami\Collector;
4+
use Clue\React\Ami\Client;
5+
use Clue\React\Ami\Factory;
76
use Clue\React\Ami\Protocol\Collection;
87

98
require __DIR__ . '/../vendor/autoload.php';
@@ -16,7 +15,7 @@
1615
$factory->createClient($target)->then(function (Client $client) use ($loop) {
1716
echo 'Successfully connected' . PHP_EOL;
1817

19-
$collector = new Collector($client);
18+
$collector = new ActionSender($client);
2019

2120
$collector->sipPeers()->then(function (Collection $collection) {
2221
var_dump('result', $collection);

src/ActionSender.php

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
namespace Clue\React\Ami;
44

55
use Clue\React\Ami\Client;
6-
use Clue\React\Ami\Protocol\Response;
7-
use Clue\React\Ami\Protocol\Action;
8-
use UnexpectedValueException;
6+
use Clue\React\Ami\Protocol\Collection;
97
use Clue\React\Ami\Protocol\Event;
8+
use Clue\React\Ami\Protocol\Response;
9+
use React\Promise\Deferred;
1010

1111
class ActionSender
1212
{
@@ -39,11 +39,6 @@ public function ping()
3939
return $this->request('Ping');
4040
}
4141

42-
public function coreShowChannels()
43-
{
44-
return $this->request('CoreShowChannels');
45-
}
46-
4742
public function command($command)
4843
{
4944
return $this->request('Command', array('Command' => $command));
@@ -62,11 +57,6 @@ public function events($eventMask)
6257
return $this->request('Events', array('EventMask' => $eventMask));
6358
}
6459

65-
public function sipPeers()
66-
{
67-
return $this->request('SIPPeers');
68-
}
69-
7060
public function sipShowPeer($peerName)
7161
{
7262
return $this->request('SIPshowpeer', array('Peer' => $peerName));
@@ -97,6 +87,32 @@ public function getConfig($filename, $category = null)
9787
return $this->request('GetConfig', array('Filename' => $filename, 'Category' => $category));
9888
}
9989

90+
/**
91+
* @return \React\Promise\PromiseInterface Promise<Collection> collection with "Event: CoreShowChannel"
92+
*/
93+
public function coreShowChannels()
94+
{
95+
return $this->collectEvents('CoreShowChannels', 'CoreShowChannelsComplete');
96+
}
97+
98+
/**
99+
*
100+
* @return \React\Promise\PromiseInterface Promise<Collection> collection with "Event: PeerEntry"
101+
*/
102+
public function sipPeers()
103+
{
104+
return $this->collectEvents('SIPPeers', 'PeerlistComplete');
105+
}
106+
107+
/**
108+
*
109+
* @return \React\Promise\PromiseInterface Promise<Collection> collection with "Event: Agents"
110+
*/
111+
public function agents()
112+
{
113+
return $this->collectEvents('Agents', 'AgentsComplete');
114+
}
115+
100116
private function boolParam($value)
101117
{
102118
if ($value === true) {
@@ -112,4 +128,44 @@ private function request($name, array $args = array())
112128
{
113129
return $this->client->request($this->client->createAction($name, $args));
114130
}
131+
132+
private function collectEvents($command, $expectedEndEvent)
133+
{
134+
$req = $this->client->createAction($command);
135+
$ret = $this->client->request($req);
136+
$id = $req->getActionId();
137+
138+
$deferred = new Deferred();
139+
140+
// collect all intermediary channel events with this action ID
141+
$collected = array();
142+
$collector = function (Event $event) use ($id, &$collected, $deferred, $expectedEndEvent) {
143+
if ($event->getActionId() === $id) {
144+
$collected []= $event;
145+
146+
if ($event->getName() === $expectedEndEvent) {
147+
$deferred->resolve($collected);
148+
}
149+
}
150+
};
151+
$this->client->on('event', $collector);
152+
153+
// unregister collector if client fails
154+
$client = $this->client;
155+
$unregister = function () use ($client, $collector) {
156+
$client->removeListener('event', $collector);
157+
};
158+
$ret->then(null, $unregister);
159+
160+
// stop waiting for events
161+
$deferred->promise()->then($unregister);
162+
163+
return $ret->then(function (Response $response) use ($deferred) {
164+
// final result has been received => merge all intermediary channel events
165+
return $deferred->promise()->then(function ($collected) use ($response) {
166+
$last = array_pop($collected);
167+
return new Collection($response, $collected, $last);
168+
});
169+
});
170+
}
115171
}

src/Collector.php

Lines changed: 0 additions & 74 deletions
This file was deleted.

src/Protocol/Collection.php

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,18 @@
22

33
namespace Clue\React\Ami\Protocol;
44

5-
use UnexpectedValueException;
6-
7-
class Collection extends Message
5+
class Collection extends Response
86
{
9-
private $response;
107
private $entryEvents;
118
private $completeEvent;
129

1310
public function __construct(Response $response, array $entryEvents, Event $completeEvent)
1411
{
1512
$this->fields = $response->getFields();
16-
$this->response = $response;
1713
$this->entryEvents = $entryEvents;
1814
$this->completeEvent = $completeEvent;
1915
}
2016

21-
public function getResponse()
22-
{
23-
return $this->response;
24-
}
25-
2617
public function getEntryEvents()
2718
{
2819
return $this->entryEvents;

0 commit comments

Comments
 (0)