Skip to content

Instantly share code, notes, and snippets.

@hctilg
Created February 25, 2025 15:07
Show Gist options
  • Save hctilg/bae4702a8a380c31d8b5f53544d207bb to your computer and use it in GitHub Desktop.
Save hctilg/bae4702a8a380c31d8b5f53544d207bb to your computer and use it in GitHub Desktop.
GitHub Checker
<?php
class GitHub {
public $username;
public function __construct(string $username) {
// Check username
if (empty($username))
die("Username should not be empty!\n");
$this->username = $username;
}
/**
* $gh = new GitHub("USERNAME");
* $gh($method, $args);
*/
public function __invoke(string $method, array $args=[]) {
return $this->get_users($method);
}
/**
* $gh = new GitHub("USERNAME");
* $gh->$method($args);
*/
public function __call(string $method, array $args=[]) {
return $this->get_users($method);
}
private function get_users(string $type = "following" | "followers") {
$len_chunk_users = 1;
$users = [];
$i = 0;
while ($len_chunk_users > 0) {
$i++;
// Get page content
$html = file_get_contents("https://github.com/{$this->username}?page={$i}&tab={$type}");
$doc = new DOMDocument();
@$doc->loadHTML($html); // @ to suppress warnings about malformed HTML
$xpath = new DOMXPath($doc);
// Find container
$container = $xpath->query("//*[@id='user-profile-frame']")->item(0);
if (!$container) break;
// Find all user elements
$elements = $xpath->query(".//div[contains(@class, 'd-table') and contains(@class, 'table-fixed') and contains(@class, 'col-12') and contains(@class, 'width-full') and contains(@class, 'py-4') and contains(@class, 'border-bottom') and contains(@class, 'color-border-muted')]", $container);
$len_elements = 0;
foreach ($elements as $element) {
try {
$len_elements++;
// Get username
$username_nodes = $xpath->query(".//span[contains(@class, 'Link--secondary')]", $element);
$username = $username_nodes->length > 0 ? trim($username_nodes->item(0)->textContent) : '';
// Get profile image
$avatar_nodes = $xpath->query(".//img[contains(@class, 'avatar')]", $element);
$profile = $avatar_nodes->length > 0 ? $avatar_nodes->item(0)->getAttribute('src') : '';
// Get name
$name_nodes = $xpath->query(".//span[contains(@class, 'f4') and contains(@class, 'Link--primary')]", $element);
$name = $name_nodes->length > 0 ? trim($name_nodes->item(0)->textContent) : '';
$users[] = [
"username" => $username,
"profile" => $profile,
"name" => $name
];
} catch (Exception $error) {
echo "Error: " . $error->getMessage() . PHP_EOL;
continue;
}
}
$len_chunk_users = $len_elements;
}
return $users;
}
}
$username = "hctilg";
if (!empty($_GET['username'])) {
$headers = @get_headers("https://github.com/" . trim($_GET['username']));
$is_404 = ($headers !== false && strpos($headers[0], '404') !== false);
if (!$is_404) $username = trim($_GET['username']);
}
$gh = new GitHub($username);
$following = $gh->following();
$followers = $gh->followers();
$following_usernames = array_column($following, 'username');
$followers_usernames = array_column($followers, 'username');
$mutual_usernames = array_intersect($following_usernames, $followers_usernames);
$unmutual_usernames_1 = array_diff($following_usernames, $followers_usernames);
$unmutual_usernames_2 = array_diff($followers_usernames, $following_usernames);
$mutual = [];
$unmutual_1 = [];
$unmutual_2 = [];
foreach ($following as $user) {
if (in_array($user['username'], $mutual_usernames)) {
$mutual[] = $user;
} elseif (in_array($user['username'], $unmutual_usernames_1)) {
$unmutual_1[] = $user;
}
}
foreach ($followers as $user) {
if (in_array($user['username'], $unmutual_usernames_2)) {
$unmutual_2[] = $user;
}
}
function user_to_html($user) {
$username = $user['username'];
$profile = $user['profile'];
$name = $user['name'];
return <<<EOF
<a class="user-box" href="https://github.com/{$username}" target="_blank">
<img class="profile" src="{$profile}" loading="lazy"/>
<div class="bio">
<span class="name">{$name}</span>
<span class="username">@{$username}</span>
</div>
<a class="scan-link" href="?username={$username}">scan</a>
</a>
EOF;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GitHub Checker</title>
<style>
* {
box-sizing: border-box;
}
body {
font-family: Arial;
}
/* Style the tab */
.tab {
overflow: hidden;
border: 1px solid #ccc;
background-color: #f1f1f1;
}
/* Style the buttons inside the tab */
.tab button {
background-color: inherit;
float: left;
border: none;
outline: none;
cursor: pointer;
padding: 14px 16px;
transition: 0.3s;
font-size: 17px;
}
/* Change background color of buttons on hover */
.tab button:hover {
background-color: #ddd;
}
/* Create an active/current tablink class */
.tab button.active {
background-color: #ccc;
}
/* Style the tab content */
.tabcontent {
display: none;
padding: 6px 12px;
border: 1px solid #ccc;
border-top: none;
}
.users-box {
display: flex;
flex-flow: column nowrap;
margin: 1rem 0.4rem;
gap: 14px;
}
.user-box {
width: 100%;
background: #fefefe;
border-radius: 3px;
padding: 10px 20px;
display: flex;
text-decoration: none;
flex-flow: row nowrap;
justify-items: center;
gap: 24px;
}
.user-box:hover {
background: #efefef;
}
.user-box .bio {
display: flex;
flex-flow: column nowrap;
justify-content: center;
align-items: center;
gap: 6px;
}
.user-box .bio .name {
font-size: 1.2rem;
color: #0f0903;
}
.user-box .bio .username {
font-size: 1rem;
color: #6e675e;
}
.user-box .profile {
width: 70px;
height: 70px;
background-color: #ffffff1a;
border-radius: 50% !important;
box-shadow: 0 0 0 1px #ffffff26;
overflow: hidden;
}
</style>
</head>
<body>
<div class="tab">
<button class="tablinks active" onclick="open_tab(event, 'following')">Following</button>
<button class="tablinks" onclick="open_tab(event, 'followers')">Followers</button>
<button class="tablinks" onclick="open_tab(event, 'mutual')">Mutual</button>
<button class="tablinks" onclick="open_tab(event, 'unmutual_1')">Unmutual (Followers - Following)</button>
<button class="tablinks" onclick="open_tab(event, 'unmutual_2')">Unmutual (Following - Followers)</button>
</div>
<div id="following" class="tabcontent" style="display: block;">
<h3>Following - <?php echo count($following) ?></h3>
<div class="users-box">
<?php foreach ($following as $user) {
echo user_to_html($user, true);
} ?>
</div>
</div>
<div id="followers" class="tabcontent">
<h3>Followers - <?php echo count($followers) ?></h3>
<div class="users-box">
<?php foreach ($followers as $user) {
echo user_to_html($user);
} ?>
</div>
</div>
<div id="mutual" class="tabcontent">
<h3>Mutual - <?php echo count($mutual) ?></h3>
<div class="users-box">
<?php foreach ($mutual as $user) {
echo user_to_html($user);
} ?>
</div>
</div>
<div id="unmutual_1" class="tabcontent">
<h3>Unmutual (Followers - Following) - <?php echo count($unmutual_1) ?></h3>
<div class="users-box">
<?php foreach ($unmutual_1 as $user) {
echo user_to_html($user);
} ?>
</div>
</div>
<div id="unmutual_2" class="tabcontent">
<h3>Unmutual (Following - Followers) - <?php echo count($unmutual_2) ?></h3>
<div class="users-box">
<?php foreach ($unmutual_2 as $user) {
echo user_to_html($user);
} ?>
</div>
</div>
<script>
function open_tab(ev, tab) {
var i, tabcontent, tablinks;
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
document.getElementById(tab).style.display = "block";
ev.currentTarget.className += " active";
}
</script>
</body>
</html>
@hctilg
Copy link
Author

hctilg commented Feb 25, 2025

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment