spatie/twitter-labs
ReactPHP-powered PHP client for Twitter Developer Labs realtime endpoints, focused on the new filtered stream API as legacy streaming is deprecated. Works without deep React knowledge but integrates with event loops; easy migration from spatie/twitter-streaming-api.
Installation:
composer require spatie/twitter-labs
Ensure ext-curl and ext-json are enabled in your PHP environment.
Configuration: Publish the config file:
php artisan vendor:publish --provider="Spatie\TwitterLabs\TwitterLabsServiceProvider"
Update .env with your Twitter API Bearer Token (from Twitter Developer Portal):
TWITTER_LAB_BEARER_TOKEN=your_bearer_token_here
First Use Case: Stream public tweets in real-time:
use Spatie\TwitterLabs\TwitterLabs;
$stream = TwitterLabs::streamPublicTweets();
$stream->on('tweet', function ($tweet) {
// Handle incoming tweet (e.g., log, store in DB, or process)
\Log::info('New tweet:', $tweet);
});
Run the stream in a Laravel command or controller (requires async handling).
config/twitter-labs.php: Customize timeouts, retries, and logging.app/Console/Commands/: Example: Create a StreamTweetsCommand to encapsulate streaming logic.Streaming Tweets:
TwitterLabs::streamPublicTweets() for real-time public tweets.$stream = TwitterLabs::streamPublicTweets(['lang' => 'en']);
Handling Events:
tweet, connect, disconnect):
$stream->on('tweet', fn($tweet) => $this->processTweet($tweet));
$stream->on('error', fn($error) => \Log::error($error));
Async Processing:
$stream->on('tweet', function ($tweet) {
dispatch(new StoreTweetJob($tweet));
});
Laravel Integration:
// app/Console/Commands/StreamTweets.php
public function handle() {
$stream = TwitterLabs::streamPublicTweets();
$this->listenForEvents($stream);
}
$stream->on('tweet', fn($tweet) => event(new TweetReceived($tweet)));
Reconnect Logic:
$stream->on('disconnect', function () use ($stream) {
sleep(2 ** $this->retryCount++);
$stream->connect();
});
Custom Endpoints:
use Spatie\TwitterLabs\TwitterLabsClient;
class CustomTwitterLabsClient extends TwitterLabsClient {
public function customEndpoint($params) {
return $this->request('GET', '/custom-endpoint', $params);
}
}
Rate Limiting:
limit event:
$stream->on('limit', fn($limit) => \Log::warning("Rate limit hit: {$limit->resource}"));
Testing:
$this->mock(TwitterLabsClient::class, function ($mock) {
$mock->shouldReceive('streamPublicTweets')
->andReturnSelf()
->shouldReceive('on')
->andReturnSelf();
});
Async Blocking:
sleep()) in event handlers—use ReactPHP’s Timer for delays:
use React\EventLoop\Timer\Timer;
$stream->on('disconnect', function () use ($loop) {
$loop->addTimer(2, fn() => $this->reconnect());
});
Bearer Token Leaks:
.env and validate the token exists in config/twitter-labs.php:
'bearer_token' => env('TWITTER_LAB_BEARER_TOKEN') ?: throw new \RuntimeException('Bearer token not set'),
Deprecated Endpoints:
Memory Leaks:
terminate() or command cleanup:
public function terminate() {
$this->stream->close();
}
Enable Logging:
config/twitter-labs.php:
'log' => [
'enabled' => true,
'channel' => 'single',
],
ReactPHP Event Loop:
React\EventLoop\Loop::get() to inspect the loop state.HTTP Errors:
$stream->on('error', function ($error) {
if ($error->getCode() === 429) {
// Retry logic for rate limits
}
});
Custom Middleware:
$client->withMiddleware(function ($request) {
$request->headers->set('X-Custom-Header', 'value');
});
Event Broadcasting:
$stream->on('tweet', fn($tweet) => broadcast(new TweetBroadcast($tweet))->toOthers());
Database Sync:
observers or model events to sync tweets with your DB:
class TweetObserver {
public function saved(Tweet $tweet) {
// Trigger a webhook or update cache
}
}
Testing Labs Features:
TwitterLabs::fake() for unit tests:
TwitterLabs::fake([
'tweets' => [/* mock data */],
]);
How can I help you explore Laravel packages today?