Last active
April 8, 2025 23:44
-
-
Save JoelLisenby/56b813ceee19b737684be2949b4575a9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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