Skip to main content

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-Welcome header on every response (declaring partnership)
  • Verify each visitor's subscription token locally, with no external network call
  • Expose a $tokenContext array to your controllers and templates

Prerequisites

  • A registered site on zeroad.network - you'll need the clientId from your site's settings
  • PHP 7.2+ or PHP 8.0+
  • ext-sodium installed 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

FlagWhen trueWhat to do
HIDE_ADVERTISEMENTSSubscriber has Clean Web + site declares itSkip rendering ad blocks
HIDE_COOKIE_CONSENT_SCREENSameSkip cookie consent banner
HIDE_MARKETING_DIALOGSSameSkip newsletter/promo popups
DISABLE_NON_FUNCTIONAL_TRACKINGSameSkip loading third-party trackers
DISABLE_CONTENT_PAYWALLSubscriber has One Pass + site declares itServe full content
ENABLE_SUBSCRIPTION_ACCESSSameTreat visitor as subscribed to your service

Full working example

A minimal plain PHP application with the integration wired up end-to-end:

index.php
<?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:

app/Http/Middleware/ZeroAdNetwork.php
<?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