heise/shariff
PHP backend for Shariff that fetches social share counts without client-side requests to social networks. Supports Buffer, Facebook, Pinterest, Reddit, Xing, VK and more, with domain whitelisting and configurable caching (filesystem or Laminas adapters).
composer require heise/shariff
routes/web.php:
Route::get('/shariff', [ShariffController::class, 'index']);
ShariffController.php) with a minimal handler:
use Heise\Shariff\Backend;
class ShariffController extends Controller
{
public function index(Request $request)
{
$options = [
'domains' => ['yourdomain.com'], // Whitelist allowed domains
'services' => ['Facebook', 'Twitter', 'LinkedIn'], // Enable required services
'cache' => ['ttl' => 3600] // Cache for 1 hour
];
$shariff = new Backend($options);
$url = $request->query('url', url()->current());
$counts = $shariff->get($url);
return response()->json($counts);
}
}
curl "http://yourdomain.com/shariff?url=https://example.com"
Use the endpoint in Blade templates or frontend JS:
<div class="share-counts">
<span data-service="facebook" data-url="{{ $articleUrl }}">0</span>
<span data-service="twitter" data-url="{{ $articleUrl }}">0</span>
</div>
<script>
document.querySelectorAll('[data-service]').forEach(el => {
fetch(`/shariff?url=${encodeURIComponent(el.dataset.url)}`)
.then(res => res.json())
.then(data => {
el.textContent = data[el.dataset.service] || '0';
});
});
</script>
Configure services individually in the Backend constructor:
$options = [
'Facebook' => [
'app_id' => env('FACEBOOK_APP_ID'),
'secret' => env('FACEBOOK_APP_SECRET')
],
'Twitter' => [
'use_legacy_api' => false // Example: Disable legacy API for Twitter
]
];
$shariff = new Backend($options);
Leverage Laravel’s cache drivers (Redis, Memcached, etc.) by implementing CacheInterface:
use Heise\Shariff\CacheInterface;
use Illuminate\Cache\CacheManager;
class LaravelCache implements CacheInterface
{
protected $cache;
public function __construct(CacheManager $cache)
{
$this->cache = $cache->store('redis');
}
public function get($key) { /* ... */ }
public function set($key, $value, $ttl) { /* ... */ }
public function delete($key) { /* ... */ }
}
$options = [
'cacheClass' => LaravelCache::class,
'cache' => ['ttl' => 3600]
];
Wrap Shariff calls in Laravel’s try-catch or use middleware:
use Illuminate\Support\Facades\RateLimiter;
Route::middleware(['throttle:100,1'])->group(function () {
Route::get('/shariff', [ShariffController::class, 'index']);
});
Enable/disable services based on config or user roles:
$services = config('shariff.services', ['Facebook', 'Twitter']);
$options = ['services' => $services];
Store share counts in a database and sync with Scout:
$counts = $shariff->get($url);
ShareCount::updateOrCreate(
['url' => $url],
['facebook' => $counts['facebook'] ?? 0]
);
Domain Whitelisting:
domains will return empty results.$request->query('url') against allowed domains:
$url = parse_url($request->query('url'), PHP_URL_HOST);
if (!in_array($url, config('shariff.domains'))) {
abort(403, 'Domain not allowed');
}
Facebook Graph API Changes:
app_id/secret for accurate counts.Caching Quirks:
Filesystem cache may fill up /tmp. Use Laravel’s cache drivers instead.CacheInterface for Redis/Memcached:
$options = [
'cacheClass' => LaravelCache::class,
'cache' => ['ttl' => 3600, 'store' => 'redis']
];
Service Failures:
try-catch in the controller:
try {
$counts = $shariff->get($url);
} catch (\Exception $e) {
Log::error($e);
return response()->json(['error' => 'Service unavailable'], 503);
}
URL Encoding:
?, #) may break parsing.$url = rawurldecode($request->query('url'));
Log Raw Responses:
Add a logger to Backend or use Laravel’s logging:
use Psr\Log\LoggerInterface;
$shariff = new Backend($options, new Logger());
Configure in config/shariff.php:
'logger' => [
'channel' => 'single',
'level' => 'debug'
],
Test with curl:
Manually test endpoints to isolate issues:
curl -v "http://yourdomain.com/shariff?url=https://example.com"
Check HTTP Headers:
Ensure User-Agent and Accept headers are set (default in Guzzle):
$options['client'] = [
'headers' => [
'User-Agent' => 'Laravel-Shariff/1.0',
'Accept' => 'application/json'
]
];
Custom Services:
Extend Heise\Shariff\Service\AbstractService to add new platforms (e.g., Mastodon):
class MastodonService extends AbstractService
{
protected $name = 'Mastodon';
protected $url = 'https://mastodon.social/api/v1/statuses';
public function getCount($url) { /* ... */ }
}
Override Cache Logic:
Replace LaminasCache with a Laravel-specific implementation:
class LaravelCache implements CacheInterface
{
public function get($key) {
return Cache::get('shariff_' . $key);
}
// ...
}
Modify Response Format: Transform raw counts into a Laravel-friendly structure:
$counts = $shariff->get($url);
return ShareCountResource::collection($counts);
Batch Requests: Use Laravel queues to fetch counts asynchronously:
ShariffJob::dispatch($url)->onQueue('shariff');
class ShariffJob implements ShouldQueue
{
public function handle() {
$counts = $shariff->get($this->url);
ShareCount::updateOrCreate([...], $counts);
}
}
Cache Invalidation:
Invalidate cache on content updates (e.g., ArticleUpdated event):
Cache::forget('shariff_' . $article->url);
Service Prioritization: Disable slower services (e.g., Pinterest) during peak traffic:
$services = config('app.debug') ? ['Facebook', 'Twitter'] : ['Facebook'];
How can I help you explore Laravel packages today?