PHP Classes

File: bin/update-psl

Recommend this page to a friend!
  Packages of Raul   Disposable Email Blocker Core   bin/update-psl   Download  
File: bin/update-psl
Role: Example script
Content type: text/plain
Description: Example script
Class: Disposable Email Blocker Core
Detect disposable or temporary email addresses
Author: By
Last change:
Date: 1 month ago
Size: 6,292 bytes
 

Contents

Class file image Download
#!/usr/bin/env php
<?php

declare(strict_types=1);

/**
 * Updates the bundled Public Suffix List (PSL) .dat file.
 *
 * Source: https://publicsuffix.org/list/public_suffix_list.dat
 *
 * Usage:
 * ./bin/update-psl [--dry-run] [--output=path] [--verbose]
 *
 * Options:
 * --dry-run Show what would be fetched without writing
 * --output=path Custom output path (default: src/Resources/public_suffix_list.dat)
 * --verbose Show progress details
 */

// Find autoloader
$autoloadPaths = [
   
__DIR__ . '/../vendor/autoload.php', // Running from package directly
   
__DIR__ . '/../../../autoload.php', // Running from vendor/bin
   
getcwd() . '/vendor/autoload.php', // Running from project root
];

$autoloaded = false;
foreach (
$autoloadPaths as $autoloadPath) {
    if (
is_file($autoloadPath)) {
        require_once
$autoloadPath;
       
$autoloaded = true;
        break;
    }
}

if (!
$autoloaded) {
   
fwrite(STDERR, "Could not find autoload.php. Run composer install first.\n");
    exit(
1);
}

use
Pdp\Rules;

// Parse arguments
$options = getopt('', ['dry-run', 'output:', 'verbose', 'help']);
$args = array_slice($argv, 1);

if (isset(
$options['help']) || in_array('--help', $args) || in_array('-h', $args)) {
    echo <<<HELP
Update Public Suffix List

Usage:
  ./bin/update-psl [options]
  vendor/bin/update-psl [options]

Options:
  --dry-run Download and validate without writing the file
  --output=path Custom output path (default: src/Resources/public_suffix_list.dat)
  --verbose Show detailed progress
  --help Show this help message

Source:
  https://publicsuffix.org/list/public_suffix_list.dat

Examples:
  ./bin/update-psl
  ./bin/update-psl --dry-run
  ./bin/update-psl --output=storage/public_suffix_list.dat
  ./bin/update-psl --verbose

HELP;
    exit(
0);
}

$dryRun = isset($options['dry-run']) || in_array('--dry-run', $args);
$verbose = isset($options['verbose']) || in_array('--verbose', $args);

// Output helpers
function info(string $message): void {
    echo
"\033[34m[INFO]\033[0m $message\n";
}

function
success(string $message): void {
    echo
"\033[32m[OK]\033[0m $message\n";
}

function
warning(string $message): void {
    echo
"\033[33m[WARN]\033[0m $message\n";
}

function
error(string $message): void {
    echo
"\033[31m[ERROR]\033[0m $message\n";
}

function
progress(string $message): void {
    global
$verbose;
    if (
$verbose) {
        echo
" ? $message\n";
    }
}

// Constants
const PSL_URL = 'https://publicsuffix.org/list/public_suffix_list.dat';
const
PSL_MIN_BYTES = 200_000;
const
PSL_MIN_LINES = 10_000;

// Main
echo "\n";
echo
"??????????????????????????????????????????????????????????????\n";
echo
"? Public Suffix List Updater ?\n";
echo
"??????????????????????????????????????????????????????????????\n";
echo
"\n";

info("Source: " . PSL_URL);

// Download
$context = stream_context_create([
   
'http' => [
       
'method' => 'GET',
       
'timeout' => 30,
       
'follow_location' => 1,
       
'user_agent' => 'disposable-email-blocker-core/bin-update-psl',
       
'header' => "Accept: text/plain\r\n",
    ],
]);

progress("Downloading...");
$startTime = microtime(true);
$content = @file_get_contents(PSL_URL, false, $context);
$elapsed = round(microtime(true) - $startTime, 2);

if (
$content === false) {
   
error("Failed to download PSL from " . PSL_URL);
    exit(
1);
}

$bytes = strlen($content);
$sizeKb = round($bytes / 1024, 1);
success("Downloaded {$sizeKb} KB in {$elapsed}s");

// Validation
echo "\n";
info("Validating downloaded content...");

if (
$bytes < PSL_MIN_BYTES) {
   
error("Content too small ({$sizeKb} KB). Expected at least " . round(PSL_MIN_BYTES / 1024) . " KB.");
    exit(
1);
}
progress("Size check passed ({$sizeKb} KB)");

$lineCount = substr_count($content, "\n");
if (
$lineCount < PSL_MIN_LINES) {
   
error("Too few lines ($lineCount). Expected at least " . number_format(PSL_MIN_LINES) . ".");
    exit(
1);
}
progress("Line count check passed (" . number_format($lineCount) . " lines)");

if (!
str_contains($content, '===BEGIN ICANN DOMAINS===')) {
   
error("Missing '===BEGIN ICANN DOMAINS===' marker.");
    exit(
1);
}
progress("ICANN section marker found");

if (!
str_contains($content, '===BEGIN PRIVATE DOMAINS===')) {
   
error("Missing '===BEGIN PRIVATE DOMAINS===' marker.");
    exit(
1);
}
progress("Private section marker found");

progress("Parsing with Rules::fromString()...");
try {
   
Rules::fromString($content);
   
success("PSL parsed successfully");
} catch (\
Throwable $e) {
   
error("PSL parse failed: " . $e->getMessage());
    exit(
1);
}

echo
"\n";

// Statistics
echo "??????????????????????????????????????????????????????????????\n";
echo
"? Statistics ?\n";
echo
"??????????????????????????????????????????????????????????????\n";
echo
"\n";
echo
" Size : {$sizeKb} KB\n";
echo
" Lines : " . number_format($lineCount) . "\n";
echo
"\n";

if (
$dryRun) {
   
warning("Dry run - no file written");
    echo
"\n";
    exit(
0);
}

// Write
$defaultOutputPath = __DIR__ . '/../src/Resources/public_suffix_list.dat';
$outputPath = $options['output'] ?? $defaultOutputPath;

$outputDir = dirname($outputPath);
if (!
is_dir($outputDir)) {
    if (!
mkdir($outputDir, 0755, true)) {
       
error("Failed to create output directory: $outputDir");
        exit(
1);
    }
}

info("Writing to: $outputPath");

$bytesWritten = file_put_contents($outputPath, $content);

if (
$bytesWritten === false) {
   
error("Failed to write file!");
    exit(
1);
}

$writtenKb = round($bytesWritten / 1024, 1);
success("Written {$writtenKb} KB to $outputPath");
echo
"\n";