Connectors are Stream's event adapters. Each one listens to WordPress (or plugin) actions and writes activity records. This guide is enough to scaffold a working connector without reading every builtin file.
Related: ARCHITECTURE.md (lifecycle) Β· connectors.md (generated inventory of builtins)
| Kind | Location |
|---|---|
| Abstract contract | classes/class-connector.php (WP_Stream\Connector) |
| Registry | classes/class-connectors.php (WP_Stream\Connectors) |
| Builtin + integrated connectors | connectors/class-connector-{slug}.php |
Builtins are listed in Connectors::BUILTIN_CONNECTOR_SLUGS. On init priority 9, Stream includes each connectors/class-connector-{slug}.php, instantiates the class, then applies the wp_stream_connectors filter so other plugins can add more.
Stream does not autoload files under docs/ or connectors/ except the slugs in that constant. A third-party connector lives in your plugin.
Extend WP_Stream\Connector and set:
$nameβ slug stored on each record (lowercase, no spaces). Example:noop.$actionsβ WordPress hook names this connector listens to.Connector::register()attachescallback()to each of them.get_label()β translated connector name shown in Stream's UI.get_context_labels()β map of context slug β translated label (the "what kind of object" column).get_action_labels()β map of action slug β translated label (created, updated, clicked, β¦).callback_{action}()β one method per hook in$actions.
Optional flags:
$register_admin(defaulttrue) β run in wp-admin.$register_frontend(defaulttrue) β run on the front end (also cron, REST, and WP-CLI, because those useis_admin() === false).is_dependency_satisfied()β returnfalseto skip registration when a required plugin is missing.
WP action in $actions
β Connector::callback()
β callback_{sanitized_action}()
β $this->log( $message, $args, $object_id, $context, $action )
The callback method name is the hook name with anything except a-z, 0-9, and _ replaced by _. So stream_noop_demo_clicked maps to callback_stream_noop_demo_clicked. A hyphenated hook such as hyphenated-action maps to callback_hyphenated_action.
log() arguments:
| Argument | Role |
|---|---|
$message |
sprintf-ready string shown in the Stream table. Extra % characters in user data must be escaped (Connector::escape_percentages()). |
$args |
Values for sprintf and stored as record meta. |
$object_id |
Related object ID, or null. |
$context |
Must be a key from get_context_labels(). |
$action |
Must be a key from get_action_labels(). |
$user_id |
Optional. Defaults to the current user. |
Connector::register() is what attaches the hooks. Call parent::register() if you override it.
Do not pass class-name strings to the registry. wp_stream_connectors receives instances keyed by $name.
Add the filter on plugins_loaded (or earlier). Connectors are constructed on init priority 9, so plugins_loaded is early enough. Registering the filter on init priority 10 is too late.
add_action( 'plugins_loaded', 'my_plugin_stream_connectors' );
function my_plugin_stream_connectors() {
add_filter( 'wp_stream_connectors', 'my_plugin_register_stream_connector' );
}
function my_plugin_register_stream_connector( $instances ) {
if ( ! class_exists( \WP_Stream\Connector::class ) ) {
return $instances;
}
require_once __DIR__ . '/class-connector-example.php';
$connector = new \WP_Stream\Connector_Example();
$instances[ $connector->name ] = $connector;
return $instances;
}Guard on class_exists( \WP_Stream\Connector::class ) so the site does not fatal when Stream is inactive.
The wiki Filter Reference and the 2014 x-team/wp-stream-connector-example plugin document wp_stream_connectors as an array of class names. That is wrong for current Stream. Pass Connector objects keyed by slug, as above.
Use this path only when the connector belongs in Stream itself (core WordPress or a bundled integration).
- Add the slug to
Connectors::BUILTIN_CONNECTOR_SLUGSinclasses/class-connectors.php. Hyphens in the slug become underscores in the class name (two-factorβConnector_Two_Factor). - Create
connectors/class-connector-{slug}.phpwithclass Connector_{Slug} extends Connectorin theWP_Streamnamespace. - Set
$name/$actions, implement the three label methods, and addcallback_{action}()methods that call$this->log(). Template:connectors/class-connector-posts.php. - Add PHPUnit coverage under
tests/phpunit/connectors/. - Regenerate the inventory with
npm run document:connectors.
Do not copy the noop sample into connectors/ or into BUILTIN_CONNECTOR_SLUGS. That sample is a third-party plugin, not a Stream builtin.
A complete sample plugin lives at docs/examples/stream-noop-connector/. Stream does not load it. WordPress will not auto-activate it from inside this directory (plugins are discovered one level under wp-content/plugins/).
-
Copy the folder into
wp-content/plugins/(WordPress only discovers plugins one level under that directory, not from inside Stream):cp -R docs/examples/stream-noop-connector wp-content/plugins/
On the wp-env stack from contributing.md:
npm run cli -- cp -R /var/www/html/wp-content/plugins/stream/docs/examples/stream-noop-connector /var/www/html/wp-content/plugins/ npm run cli -- wp plugin activate stream-noop-connector --network
-
Otherwise activate it from Plugins in wp-admin.
-
In wp-admin open Tools β Stream Noop Demo.
-
Click Log a noop event.
-
Open Stream. You should see one record: connector Noop (example), context Demo, action Clicked, message
Noop demo button clicked.
stream-noop-connector.phpregistersstream_noop_connector_register()onwp_stream_connectorsfromplugins_loaded, adds a Tools page, and handlesadmin-post.phpwith a nonce andmanage_options.class-connector-noop.phpis the connector:$name = 'noop', one actionstream_noop_demo_clicked,$register_frontend = false.- Submitting the button runs
do_action( 'stream_noop_demo_clicked' )βcallback_stream_noop_demo_clicked()β$this->log( β¦, 'demo', 'clicked' ).
Copy those two files into your own plugin, rename the slug / hooks / text domain, and replace the demo button with real events.