Welcome to the Developer Documentation for the GitHub User Viewer. This document explains the architecture of the application, how classes are connected, the design patterns utilized, and the data flows between the components.
The application is structured around the Single Responsibility Principle (SRP) and the Strategy Pattern to handle different HTTP request methods. Concerns are cleanly separated into Models, Clients, Fetchers, Exporters, and Utilities.
classDiagram
direction TB
class ProfileFetcherInterface {
<<interface>>
+fetch(array usernames) array
}
class SequentialFetcher {
-GitHubClient client
+fetch(array usernames) array
}
class ParallelFetcher {
-GitHubClient client
+fetch(array usernames) array
}
class GitHubClient {
-string token
+createHandle(string url) CurlHandle|false
+createHandleForUser(string username) CurlHandle|false
+fetchProfile(string username) UserProfile
+parseResponse(string response, CurlHandle ch, string defaultUsername) UserProfile
}
class UserProfile {
+string username
+bool success
+string name
+string avatarUrl
+string htmlUrl
+string bio
+string company
+string location
+int followers
+int following
+int publicRepos
+string error
+int|null rateLimitRemaining
+int|null rateLimitLimit
+fromFailure(string username, string error) UserProfile
+toArray() array
}
class UsernameCleaner {
+clean(string input) array
}
class CsvExporter {
+export(array results) void
}
ProfileFetcherInterface <|.. SequentialFetcher
ProfileFetcherInterface <|.. ParallelFetcher
SequentialFetcher --> GitHubClient : depends on
ParallelFetcher --> GitHubClient : depends on
GitHubClient ..> UserProfile : creates
CsvExporter ..> UserProfile : exports data from
We define a common ProfileFetcherInterface contract.
SequentialFetcherimplements standard sequential queries.ParallelFetcherimplements parallel queries using Multi-cURL. This allows the client code inindex.phpto run both strategies side-by-side or swap them out without modifying the execution details of the controller.
The UserProfile class encapsulates the structured profile response from the GitHub API. Rather than passing raw associative arrays containing unchecked keys, the application maps raw JSON to a strongly-typed UserProfile class representation.
The client (GitHubClient) is injected into the fetchers via their constructors. This decouples the network-calling mechanism from the orchestration of multiple requests, making components modular and testable.
- Configures cURL handles with GitHub headers, agent parameters, and rate-limiting authorization tokens.
- Extracts HTTP response headers (e.g. rate limit states) and decodes bodies.
- Handles API errors gracefully (e.g. mapping
404to"User not found",403to"Rate limit exceeded").
- Stores specific user profile data.
- Exposes
toArray()to support the front controller and keep compatibility with the client-side JavaScript (script.js).
- Takes raw text input from the user (from commas, newlines, tabs, spaces), sanitizes it, and runs a case-insensitive de-duplication loop.
- Iterates through profile records and outputs headers to immediately trigger file streams (
php://output) to prompt a user download.
When a user submits a list of developer usernames, the following pipeline executes:
sequenceDiagram
autonumber
actor User as User Browser
participant Index as index.php
participant Cleaner as UsernameCleaner
participant Client as GitHubClient
participant Seq as SequentialFetcher
participant Parallel as ParallelFetcher
User->>Index: POST: usernames & token
Index->>Cleaner: clean(usernamesInput)
Cleaner-->>Index: array of clean usernames
Index->>Client: __construct(githubToken)
Index->>Seq: __construct(client)
Index->>Seq: fetch(cleanUsernames)
loop For each username
Seq->>Client: fetchProfile(username)
Client-->>Seq: UserProfile instance
end
Seq-->>Index: Sequential results & duration
Index->>Parallel: __construct(client)
Index->>Parallel: fetch(cleanUsernames)
Parallel->>Client: createHandleForUser(username) for all profiles
Parallel->>Parallel: curl_multi_exec() loops
Parallel->>Client: parseResponse() for all handles
Client-->>Parallel: UserProfile instances
Parallel-->>Index: Parallel results & duration
Index-->>User: Render comparative HTML and graphs
- When the user clicks the folder icon (📂) next to a developer in the directory:
- Javascript (
script.js) intercepts the click and makes a fetch request toindex.php?action=repos&username={username}. index.phpinstantiatesGitHubClientand obtains a cURL handle forhttps://api.github.com/users/{username}/repos?per_page=30.- The proxy request executes, extracts the body payload, and prints it back to the client as JSON.
- Javascript renders the repository list inside a modal overlay.
- Javascript (
This codebase uses automated tools to maintain code quality, security, and standards.
The codebase adheres to the PSR-12 Extended Coding Style Guide. Coding standard compliance is enforced in the CI/CD pipeline using PHP_CodeSniffer.
To run the style checker locally:
composer lintTo automatically format style violations using PHPCBF:
vendor/bin/phpcbf --standard=PSR12 src/ tests/Unit and integration tests are written using PHPUnit and are located in the /tests directory.
To run the full test suite locally:
composer testTo test the API client logic without hitting the live GitHub API rate limits, we use namespace-level function overriding.
- Inside the test files, we override PHP's native cURL functions (e.g.,
curl_errno,curl_getinfo,curl_multi_exec,curl_multi_getcontent) within theApp\ClientandApp\Fetchernamespaces. - This intercepts calls when PHP resolves the functions, returning mocked response headers, status codes (such as
200,403,404), and error numbers (like timeouts). - This ensures test execution is extremely fast (~20ms) and works perfectly without an active internet connection.
Make sure the Parallel Speedup metric remains accurate. Parallel cURL requests should run in a time frame close to: