PHP
The zeroad.network/token Composer package adds Zero Ad Network integration to any PHP 7.2+ application - Laravel, Symfony, plain PHP, or any other framework.
By the end of this guide your site will:
- Send the
X-Better-Web-Welcomeheader on every response (declaring partnership) - Verify each visitor's subscription token locally, with no external network call
- Expose a
$tokenContextarray to your controllers and templates
Prerequisites
- A registered site on zeroad.network - you'll need the
clientIdfrom your site's settings - PHP 7.2+ or PHP 8.0+
ext-sodiuminstalled on your PHP runtime (php -m | grep sodium)
Install
composer require zeroad.network/token
Initialize once at startup
Create the Site instance once - in a service container, bootstrap file, or global config - not inside a request handler.
use ZeroAd\Token\Site;
use ZeroAd\Token\Constants;
$site = new Site([
'clientId' => 'YOUR_CLIENT_ID', // from zeroad.network/publisher/sites
'features' => [
Constants::FEATURE['CLEAN_WEB'],
// Constants::FEATURE['ONE_PASS'], // add if you support paywall bypass
],
]);
Add middleware
In your framework's middleware layer (or at the top of every request handler), do two things:
// 1. Inject your site's partner header into the response
header("{$site->SERVER_HEADER_NAME}: {$site->SERVER_HEADER_VALUE}");
// 2. Parse the subscriber token - returns an array of feature flags
$tokenContext = $site->parseToken($_SERVER[$site->CLIENT_HEADER_NAME] ?? null);
parseToken verifies the token's ED25519 signature locally. If no token is present, expired, or invalid, all flags are false. No special handling needed for non-subscribers.
Use the feature flags
$tokenContext is a flat array of booleans. Use them directly in your controllers and templates:
// Controller
$article = Article::find($id);
return view('article', [
'content' => $tokenContext['DISABLE_CONTENT_PAYWALL'] ? $article->fullContent : $article->excerpt,
'showPaywall'=> !$tokenContext['DISABLE_CONTENT_PAYWALL'],
'showAds' => !$tokenContext['HIDE_ADVERTISEMENTS'],
]);
// API endpoint
header('Content-Type: application/json');
echo json_encode([
'title' => $article->title,
'content' => $tokenContext['DISABLE_CONTENT_PAYWALL'] ? $article->fullContent : $article->excerpt,
'isPaywalled'=> !$tokenContext['DISABLE_CONTENT_PAYWALL'],
]);
Available flags
| Flag | When true | What to do |
|---|---|---|
HIDE_ADVERTISEMENTS | Subscriber has Clean Web + site declares it | Skip rendering ad blocks |
HIDE_COOKIE_CONSENT_SCREEN | Same | Skip cookie consent banner |
HIDE_MARKETING_DIALOGS | Same | Skip newsletter/promo popups |
DISABLE_NON_FUNCTIONAL_TRACKING | Same | Skip loading third-party trackers |
DISABLE_CONTENT_PAYWALL | Subscriber has One Pass + site declares it | Serve full content |
ENABLE_SUBSCRIPTION_ACCESS | Same | Treat visitor as subscribed to your service |
Full working example
A minimal plain PHP application with the integration wired up end-to-end:
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use ZeroAd\Token\Site;
use ZeroAd\Token\Constants;
$site = new Site([
'clientId' => 'YOUR_CLIENT_ID',
'features' => [Constants::FEATURE['CLEAN_WEB'], Constants::FEATURE['ONE_PASS']],
]);
// Inject partner header + parse subscriber token on every request
header("{$site->SERVER_HEADER_NAME}: {$site->SERVER_HEADER_VALUE}");
$tokenContext = $site->parseToken($_SERVER[$site->CLIENT_HEADER_NAME] ?? null);
// Route handling
$uri = $_SERVER['REQUEST_URI'];
if ($uri === '/') {
// Use $tokenContext flags to tailor the page
$showAds = !$tokenContext['HIDE_ADVERTISEMENTS'];
$fullContent = $tokenContext['DISABLE_CONTENT_PAYWALL'];
echo '<pre>' . htmlspecialchars(json_encode($tokenContext, JSON_PRETTY_PRINT)) . '</pre>';
} elseif ($uri === '/api') {
header('Content-Type: application/json');
echo json_encode(['tokenContext' => $tokenContext]);
} else {
http_response_code(404);
echo 'Not Found';
}
Full runnable examples are in the GitHub repository.
Using with Laravel
In Laravel, create a middleware class and register it globally:
<?php
namespace App\Http\Middleware;
use Closure;
use ZeroAd\Token\Site;
use ZeroAd\Token\Constants;
class ZeroAdNetwork
{
private Site $site;
public function __construct()
{
$this->site = new Site([
'clientId' => config('services.zeroad.client_id'),
'features' => [Constants::FEATURE['CLEAN_WEB']],
]);
}
public function handle($request, Closure $next)
{
$clientHeader = strtoupper(str_replace('-', '_', $this->site->CLIENT_HEADER_NAME));
$tokenContext = $this->site->parseToken($_SERVER["HTTP_{$clientHeader}"] ?? null);
$request->merge(['tokenContext' => $tokenContext]);
$response = $next($request);
$response->headers->set($this->site->SERVER_HEADER_NAME, $this->site->SERVER_HEADER_VALUE);
return $response;
}
}
Then in a controller or Blade template:
$tokenContext = $request->input('tokenContext', []);
// Blade template
@if(!$tokenContext['HIDE_ADVERTISEMENTS'])
{{-- render ad block --}}
@endif