Skip to content

Commit ddf67fc

Browse files
author
epriestley
committed
Add an example event listener, improve documentation, and add a commit discovery event
Summary: Improve documentation around Phabricator events. Test Plan: Generated and read documentation. Ran test script. Reviewers: btrahan, jungejason Reviewed By: jungejason CC: aran Maniphest Tasks: T1092 Differential Revision: https://secure.phabricator.com/D2917
1 parent 61b79b5 commit ddf67fc

File tree

8 files changed

+322
-10
lines changed

8 files changed

+322
-10
lines changed

scripts/util/emit_test_event.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/usr/bin/env php
2+
<?php
3+
4+
/*
5+
* Copyright 2012 Facebook, Inc.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
20+
$root = dirname(dirname(dirname(__FILE__)));
21+
require_once $root.'/scripts/__init_script__.php';
22+
23+
$args = new PhutilArgumentParser($argv);
24+
$args->setTagline('emit a test event');
25+
$args->setSynopsis(<<<EOHELP
26+
**emit_test_event.php** [--listen listener] ...
27+
Emit a test event after installing any specified __listener__s.
28+
EOHELP
29+
);
30+
$args->parseStandardArguments();
31+
$args->parse(
32+
array(
33+
array(
34+
'name' => 'listen',
35+
'param' => 'listener',
36+
'repeat' => true,
37+
),
38+
));
39+
40+
$console = PhutilConsole::getConsole();
41+
foreach ($args->getArg('listen') as $listener) {
42+
$console->writeOut("Installing '%s'...\n", $listener);
43+
newv($listener, array())->register();
44+
}
45+
46+
47+
$console->writeOut("Emitting event...\n");
48+
49+
PhutilEventEngine::dispatchEvent(
50+
new PhabricatorEvent(
51+
PhabricatorEventType::TYPE_TEST_DIDRUNTEST,
52+
array(
53+
'time' => time(),
54+
)));
55+
56+
$console->writeOut("Done.\n");
57+
exit(0);

src/__phutil_library_map__.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,7 @@
632632
'PhabricatorEvent' => 'infrastructure/events/PhabricatorEvent.php',
633633
'PhabricatorEventEngine' => 'infrastructure/events/PhabricatorEventEngine.php',
634634
'PhabricatorEventType' => 'infrastructure/events/constant/PhabricatorEventType.php',
635+
'PhabricatorExampleEventListener' => 'infrastructure/events/PhabricatorExampleEventListener.php',
635636
'PhabricatorFeedBuilder' => 'applications/feed/builder/PhabricatorFeedBuilder.php',
636637
'PhabricatorFeedConstants' => 'applications/feed/constants/PhabricatorFeedConstants.php',
637638
'PhabricatorFeedController' => 'applications/feed/controller/PhabricatorFeedController.php',
@@ -1647,9 +1648,12 @@
16471648
'PhabricatorErrorExample' => 'PhabricatorUIExample',
16481649
'PhabricatorEvent' => 'PhutilEvent',
16491650
'PhabricatorEventType' => 'PhutilEventType',
1651+
'PhabricatorExampleEventListener' => 'PhutilEventListener',
16501652
'PhabricatorFeedController' => 'PhabricatorController',
16511653
'PhabricatorFeedDAO' => 'PhabricatorLiskDAO',
16521654
'PhabricatorFeedPublicStreamController' => 'PhabricatorFeedController',
1655+
'PhabricatorFeedQuery' => 'PhabricatorIDPagedPolicyQuery',
1656+
'PhabricatorFeedStory' => 'PhabricatorPolicyInterface',
16531657
'PhabricatorFeedStoryAggregate' => 'PhabricatorFeedStory',
16541658
'PhabricatorFeedStoryAudit' => 'PhabricatorFeedStory',
16551659
'PhabricatorFeedStoryCommit' => 'PhabricatorFeedStory',

src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,15 @@ private function recordCommit(
374374
}
375375

376376
$this->setCache($repository, $commit_identifier);
377+
378+
PhutilEventEngine::dispatchEvent(
379+
new PhabricatorEvent(
380+
PhabricatorEventType::TYPE_DIFFUSION_DIDDISCOVERCOMMIT,
381+
array(
382+
'repository' => $repository,
383+
'commit' => $commit,
384+
)));
385+
377386
} catch (AphrontQueryDuplicateKeyException $ex) {
378387
$commit->killTransaction();
379388
// Ignore. This can happen because we discover the same new commit

src/docs/userguide/events.diviner

Lines changed: 177 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,68 @@ Phabricator allows you to install custom runtime event listeners which can react
99
to certain things happening (like a Maniphest Task being edited) and run custom
1010
code to perform logging, synchronize with other systems, or modify workflows.
1111

12-
NOTE: This feature is new and experimental, so few events are available and
13-
things might not be completely stable.
12+
These listeners are PHP classes which you install beside Phabricator, and which
13+
Phabricator loads at runtime and runs in-process. They are the most direct and
14+
powerful way to respond to events.
15+
16+
= Installing Event Listeners =
17+
18+
To install event listeners, follow these steps:
19+
20+
- Write a listener class which extends @{class:PhutilEventListener}.
21+
- Add it to a libphutil library, or create a new library (for instructions,
22+
see @{article:libphutil Libraries User Guide}.
23+
- Configure Phabricator to load the library by adding it to `load-libraries`
24+
in the Phabricator config.
25+
- Configure Phabricator to install the event listener by adding the class
26+
name to `events.listeners` in the Phabricator config.
27+
28+
You can verify your listener is registered in the "Events" tab of DarkConsole.
29+
It should appear at the top under "Registered Event Listeners". You can also
30+
see any events the page emitted there. For details on DarkConsole, see
31+
@{article:Using DarkConsole}.
32+
33+
= Example Listener =
34+
35+
Phabricator includes an example event listener,
36+
@{class:PhabricatorExampleEventListener}, which may be useful as a starting
37+
point in developing your own listeners. This listener listens for a test
38+
event that is emitted by the script `scripts/util/emit_test_event.php`.
39+
40+
If you run this script normally, it should output something like this:
41+
42+
$ ./scripts/util/emit_test_event.php
43+
Emitting event...
44+
Done.
45+
46+
This is because there are no listeners for the event, so nothing reacts to it
47+
when it is emitted. You can add the example listener by either adding it to
48+
your `events.listeners` configuration or with the `--listen` command-line flag:
49+
50+
$ ./scripts/util/emit_test_event.php --listen PhabricatorExampleEventListener
51+
Installing 'PhabricatorExampleEventListener'...
52+
Emitting event...
53+
PhabricatorExampleEventListener got test event at 1341344566
54+
Done.
55+
56+
This time, the listener was installed and had its callback invoked when the
57+
test event was emitted.
1458

1559
= Available Events =
1660

17-
== PhabricatorEventType::TYPE_MANIPHEST_WILLEDITTASK ==
61+
You can find a list of all Phabricator events in @{class:PhabricatorEventType}.
62+
63+
== All Events ==
64+
65+
The special constant `PhutilEventType::TYPE_ALL` will let you listen for all
66+
events. Normally, you want to listen only to specific events, but if you're
67+
writing a generic handler you can listen to all events with this constant
68+
rather than by enumerating each event.
69+
70+
== Maniphest: Will Edit Task ==
71+
72+
The constant for this event is
73+
`PhabricatorEventType::TYPE_MANIPHEST_WILLEDITTASK`.
1874

1975
This event is dispatched before a task is edited, and allows you to respond to
2076
or alter the edit. Data available on this event:
@@ -26,18 +82,134 @@ or alter the edit. Data available on this event:
2682
- ##mail## If this edit originates from email, the
2783
@{class:PhabricatorMetaMTAReceivedMail} object.
2884

29-
== PhabricatorEventType::TYPE_DIFFERENTIAL_WILLSENDMAIL ==
85+
This is similar to the next event (did edit task) but occurs before the edit
86+
begins.
87+
88+
== Maniphest: Did Edit Task ==
89+
90+
The constant for this event is
91+
`PhabricatorEventType::TYPE_MANIPHEST_DIDEDITTASK`.
92+
93+
This event is dispatched after a task is edited, and allows you to react to the
94+
edit. Data available on this event:
95+
96+
- ##task## The @{class:ManiphestTask} that was edited.
97+
- ##transactions## The list of edits (objects of class
98+
@{class:ManiphestTransaction}) that were applied.
99+
- ##new## A boolean indicating if this task was newly created.
100+
- ##mail## If this edit originates from email, the
101+
@{class:PhabricatorMetaMTAReceivedMail} object.
102+
103+
This is similar to the previous event (will edit task) but occurs after the
104+
edit completes.
105+
106+
== Differential: Will Send Mail ==
107+
108+
The constant for this event is
109+
`PhabricatorEventType::TYPE_DIFFERENTIAL_WILLSENDMAIL`
30110

31111
This event is dispatched before Differential sends an email, and allows you to
32112
edit the message that will be sent. Data available on this event:
33113

34114
- ##mail## The @{class:PhabricatorMetaMTAMail} being edited.
35115

36-
== PhabricatorEventType::TYPE_DIFFERENTIAL_WILLMARKGENERATED ==
116+
== Differential: Will Mark Generated ==
117+
118+
The constant for this event is
119+
`PhabricatorEventType::TYPE_DIFFERENTIAL_WILLMARKGENERATED`.
37120

38121
This event is dispatched before Differential decides if a file is generated (and
39122
doesn't need to be reviewed) or not. Data available on this event:
40123

41124
- ##corpus## Body of the file.
42125
- ##is_generated## Boolean indicating if this file should be treated as
43126
generated.
127+
128+
== Diffusion: Did Discover Commit ==
129+
130+
The constant for this event is
131+
`PhabricatorEventType::TYPE_DIFFUSION_DIDDISCOVERCOMMIT`.
132+
133+
This event is dispatched when the daemons discover a commit for the first time.
134+
This event happens very early in the pipeline, and not all commit information
135+
will be available yet. Data available on this event:
136+
137+
- `commit` The @{class:PhabricatorRepositoryCommit} that was discovered.
138+
- `repository` The @{class:PhabricatorRepository} the commit was discovered
139+
in.
140+
141+
== Edge: Will Edit Edges ==
142+
143+
NOTE: Edge events are low-level events deep in the core. It is more difficult to
144+
correct implement listeners for these events than for higher-level events.
145+
146+
The constant for this event is
147+
`PhabricatorEventType::TYPE_EDGE_WILLEDITEDGES`.
148+
149+
This event is dispatched before @{class:PhabricatorEdgeEditor} makes an edge
150+
edit.
151+
152+
- `id` An identifier for this edit operation.
153+
- `add` A list of dictionaries, each representing a new edge.
154+
- `rem` A list of dictionaries, each representing a removed edge.
155+
156+
This is similar to the next event (did edit edges) but occurs before the
157+
edit begins.
158+
159+
== Edge: Did Edit Edges ==
160+
161+
The constant for this event is
162+
`PhabricatorEventType::TYPE_EDGE_DIDEDITEDGES`.
163+
164+
This event is dispatched after @{class:PhabricatorEdgeEditor} makes an edge
165+
edit, but before it commits the transactions. Data available on this event:
166+
167+
- `id` An identifier for this edit operation. This is the same ID as
168+
the one included in the corresponding "will edit edges" event.
169+
- `add` A list of dictionaries, each representing a new edge.
170+
- `rem` A list of dictionaries, each representing a removed edge.
171+
172+
This is similar to the previous event (will edit edges) but occurs after the
173+
edit completes.
174+
175+
== Test: Did Run Test ==
176+
177+
The constant for this event is
178+
`PhabricatorEventType::TYPE_TEST_DIDRUNTEST`.
179+
180+
This is a test event for testing event listeners. See above for details.
181+
182+
= Debugging Listeners =
183+
184+
If you're having problems with your listener, try these steps:
185+
186+
- If you're getting an error about Phabricator being unable to find the
187+
listener class, make sure you've added it to a libphutil library and
188+
configured Phabricator to load the library with `load-libraries`.
189+
- Make sure the listener is registered. It should appear in the "Events" tab
190+
of DarkConsole. If it's not there, you may have forgotten to add it to
191+
`events.listeners`.
192+
- Make sure it calls `listen()` on the right events in its `register()`
193+
method. If you don't listen for the events you're interested in, you
194+
won't get a callback.
195+
- Make sure the events you're listening for are actually happening. If they
196+
occur on a normal page they should appear in the "Events" tab of
197+
DarkConsole. If they occur on a POST, you could add a `phlog()`
198+
to the source code near the event and check your error log to make sure the
199+
code ran.
200+
- You can check if your callback is getting invoked by adding `phlog()` with
201+
a message and checking the error log.
202+
- You can try listening to `PhutilEventType::TYPE_ALL` instead of a specific
203+
event type to get all events, to narrow down whether problems are caused
204+
by the types of events you're listening to.
205+
- You can edit the `emit_test_event.php` script to emit other types of
206+
events instead, to test that your listener reacts to them properly. You
207+
might have to use fake data, but this gives you an easy way to test the
208+
at least the basics.
209+
210+
= Next Steps =
211+
212+
Continue by:
213+
214+
- taking a look at @{class:PhabricatorExampleEventListener}; or
215+
- building a library with @{article:@{article:libphutil Libraries User Guide}.

src/infrastructure/events/PhabricatorEvent.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
* limitations under the License.
1717
*/
1818

19+
20+
/**
21+
* @group events
22+
*/
1923
final class PhabricatorEvent extends PhutilEvent {
2024

2125
private $user;

src/infrastructure/events/PhabricatorEventEngine.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
* limitations under the License.
1717
*/
1818

19+
/**
20+
* @group events
21+
*/
1922
final class PhabricatorEventEngine {
2023

2124
public static function initialize() {
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
/*
4+
* Copyright 2012 Facebook, Inc.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
/**
20+
* Example event listener. For details about installing Phabricator event hooks,
21+
* refer to @{article:Events User Guide: Installing Event Listeners}.
22+
*
23+
* @group events
24+
*/
25+
final class PhabricatorExampleEventListener extends PhutilEventListener {
26+
27+
public function register() {
28+
// When your listener is installed, its register() method will be called.
29+
// You should listen() to any events you are interested in here.
30+
31+
$this->listen(PhabricatorEventType::TYPE_TEST_DIDRUNTEST);
32+
}
33+
34+
public function handleEvent(PhutilEvent $event) {
35+
// When an event you have called listen() for in your register() method
36+
// occurs, this method will be invoked. You should respond to the event.
37+
38+
// In this case, we just echo a message out so the event test script will
39+
// do something visible.
40+
41+
$console = PhutilConsole::getConsole();
42+
$console->writeOut(
43+
"PhabricatorExampleEventListener got test event at %d\n",
44+
$event->getValue('time'));
45+
}
46+
47+
}
48+
49+
50+
51+
52+

0 commit comments

Comments
 (0)