Skip to content

Instantly share code, notes, and snippets.

@JoelLisenby
Last active April 8, 2025 23:44
Show Gist options
  • Save JoelLisenby/56b813ceee19b737684be2949b4575a9 to your computer and use it in GitHub Desktop.
Save JoelLisenby/56b813ceee19b737684be2949b4575a9 to your computer and use it in GitHub Desktop.
<?php
// get_wpengine_installs.php
// v1.3.0
// Example run:
// php -f get_wpengine_installs.php
//
// acquire your username and password from https://my.wpengine.com/profile/api_access
//
// Required: save public_suffix_list.dat alongside the script.
// https://publicsuffix.org/list/public_suffix_list.dat
define('API_USERNAME','');
define('API_PASSWORD','');
$file = "wpe_sites.csv";
$result_obj = run_query( 'https://api.wpengineapi.com/v1/installs' );
$runs = 1;
$advanced_network_ips = array( '141.193.213.10', '141.193.213.11' );
file_put_contents($file, 'account,install name,domain,base domain,redirects to,primary domain,dns a records,dns ns records,ns providers,wpe edge,advanced network,cname,php,multisite'."\r\n");
$current_item = 0;
while( !empty( $result_obj->next ) && $runs < 2000 ) {
$total = $result_obj->count;
$timePerItem = 0; // seconds
$totalTime = 0; // seconds
foreach( $result_obj->results as $install ) {
$current_item++;
$start = microtime(true);
progress($total, $current_item, $timePerItem, $totalTime);
if( $install->environment == 'production' && $install->status == 'active' ) {
ob_start();
$account_result_obj = run_query( 'https://api.wpengineapi.com/v1/accounts/'. $install->account->id );
$install_domains = run_query( 'https://api.wpengineapi.com/v1/installs/'. $install->id .'/domains' );
foreach( $install_domains->results as $domain ) {
$base_domain = getBaseDomain( $domain->name );
// account
echo $account_result_obj->name .',';
// install name
echo $install->name .',';
// domain
echo $domain->name .',';
// base domain
echo $base_domain .',';
// redirects to
if( !empty( $domain->redirect_to ) && property_exists( $domain->redirect_to, 'name' ) ) {
echo $domain->redirect_to->name .',';
} else {
echo ',';
}
// primary domain
echo $domain->primary == true ? 'primary,' : ',';
// dns a records
$dns_records_a = @dns_get_record( $domain->name, DNS_A );
if( $dns_records_a !== false && !empty( $dns_records_a ) ) {
$a_record_ips = array();
$cnt = 0;
foreach( $dns_records_a as $dns_record_a ) {
$cnt++;
echo $dns_record_a['ip'].($cnt < count( $dns_records_a ) ? '|' : '' );
$a_record_ips[] = trim( $dns_record_a['ip'] );
}
echo ",";
} else {
echo ',';
}
// dns ns records, ns provider
$dns_records_ns = @dns_get_record( $base_domain, DNS_NS );
if( $dns_records_ns !== false && !empty( $dns_records_ns ) ) {
$ns_record_hosts = array();
$cnt = 0;
$ns_string = '';
$ns_provider_string = '';
foreach( $dns_records_ns as $dns_record_ns ) {
$cnt++;
$provider = getBaseDomain( $dns_record_ns['target'] );
$ns_string .= !empty( $dns_record_ns['target'] ) ? $dns_record_ns['target'] .($cnt < count( $dns_records_ns ) ? '|' : '' ) : '';
$ns_provider_string .= !empty( $provider ) ? $provider .($cnt < count( $dns_records_ns ) ? '|' : '' ) : '';
}
echo $ns_string .",". $ns_provider_string .',';
} else {
echo ',,';
}
// wpe edge
echo getWpeEdgeValue($domain->name) .',';
// advanced network
echo !empty( $a_record_ips ) ? jbl_in_array( $a_record_ips, $advanced_network_ips ) .',' : ',';
// cname
echo $install->cname .',';
// php
echo $install->php_version .',';
// multisite
echo $install->is_multisite;
echo "\r\n";
}
file_put_contents($file, ob_get_clean(), FILE_APPEND);
}
$end = microtime(true);
$timePerItem = $end - $start;
$totalTime += $timePerItem;
}
$result_obj = run_query( $result_obj->next );
$runs++;
}
function getBaseDomain($url, $pslFile = 'public_suffix_list.dat') {
// Static variable to cache the public suffixes
static $publicSuffixes = null;
// Load and parse the PSL file once
if ($publicSuffixes === null) {
$publicSuffixes = [];
if (file_exists($pslFile)) {
$lines = file($pslFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
// Skip comments
if (strpos($line, '//') === 0) {
continue;
}
$suffix = trim($line);
if (!empty($suffix)) {
$publicSuffixes[$suffix] = true;
}
}
} else {
// Fallback to simple heuristic if PSL file is missing
return getBaseDomainSimple($url);
}
}
// Parse the URL to get its components
$parsed = parse_url($url);
// Check if the host exists; return empty string if not
if (!isset($parsed['path'])) {
return '';
}
// Get the host and convert to lowercase
$host = strtolower($parsed['path']);
// Split the host into parts by dots
$parts = explode('.', $host);
$numParts = count($parts);
// Find the longest matching public suffix
for ($i = 1; $i <= $numParts; $i++) {
$suffix = implode('.', array_slice($parts, -$i));
if (isset($publicSuffixes[$suffix])) {
// If there’s a label before the suffix, return it plus the suffix
if ($i < $numParts) {
return implode('.', array_slice($parts, -($i + 1), $i + 1));
}
// If the entire host is a public suffix, return the host
return $host;
}
}
// Fallback: return the last two parts if no suffix matches
return $numParts >= 2 ? implode('.', array_slice($parts, -2)) : $host;
}
// Fallback function if PSL file is unavailable
function getBaseDomainSimple($url) {
$parsed = parse_url($url);
if (!isset($parsed['host'])) {
return '';
}
$host = strtolower($parsed['host']);
$parts = explode('.', $host);
$numParts = count($parts);
return $numParts >= 2 ? implode('.', array_slice($parts, -2)) : $host;
}
function jbl_in_array( $needles, $haystack ) {
$output = 0;
foreach( $needles as $needle ) {
foreach( $haystack as $hay ) {
if( $needle == $hay ) {
$output = 1;
}
}
}
return $output;
}
// curl -sIL domain.com/-wpe-cdncheck- | grep -i X-WPE
// returns X-WPE-Edge: AN
function getWpeEdgeValue($domain) {
$url = "https://" . $domain . "/-wpe-cdncheck-";
// Initialize curl
$ch = curl_init();
// Set curl options
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_NOBODY, true); // HEAD request
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
// Execute curl request
$response = curl_exec($ch);
// Close curl handle
curl_close($ch);
if ($response === false) {
return '';
}
// Split response into lines
$lines = explode("\n", trim($response));
// Look for X-WPE-Edge header
foreach ($lines as $line) {
if (stripos($line, 'X-WPE-Edge:') !== false) {
$parts = explode(':', trim($line), 2);
if (count($parts) === 2) {
return trim($parts[1]);
}
}
}
return '';
}
function run_query( $url ) {
$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, 'GET' );
$headers = array();
$cred_string = API_USERNAME .":". API_PASSWORD;
$headers[] = "Authorization: Basic " . base64_encode($cred_string);
curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
$result = curl_exec( $ch );
if ( curl_errno( $ch ) ) {
echo 'Error:' . curl_error( $ch );
return false;
}
curl_close( $ch );
return json_decode( $result );
}
function progress($total, $current, $timePerItem, $totalTime) {
// Calculate progress
$percent = ($current / $total) * 100;
$bar = str_repeat('█', $percent / 2) . str_repeat('░', 50 - $percent / 2);
$avgTime = $totalTime / $current; // Average time per item so far
$etaSeconds = $avgTime * ($total - $current); // ETA in seconds
$lastTime = $timePerItem;
// Convert ETA to minutes and seconds
$etaMinutes = floor($etaSeconds / 60);
$etaSecs = $etaSeconds % 60;
$etaDisplay = sprintf("%dm %02ds", $etaMinutes, $etaSecs);
// Convert total time to minutes and seconds
$ttMinutes = floor($totalTime / 60);
$ttSecs = $totalTime % 60;
$ttDisplay = sprintf("%dm %02ds", $ttMinutes, $ttSecs);
// Terminal display
echo sprintf( "\033[K\r[%s] %.1f%% | Install: %d/%d | Last: %.3fs | Avg Per: %.3fs | Total: %s | ETA: %s",
$bar, $percent, $current, $total, $lastTime, $avgTime, $ttDisplay, $etaDisplay );
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment