PHP code example of zero-to-prod / github-sdk

1. Go to this page and download the library: Download zero-to-prod/github-sdk library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

zero-to-prod / github-sdk example snippets


use Zerotoprod\GitHubSdk\GitHubSdk;
use Zerotoprod\GitHubSdk\GitHubSdkConfig;

$api = new GitHubSdk([
    GitHubSdkConfig::url     => 'https://api.github.com',
    GitHubSdkConfig::headers => [
        'Authorization' => 'Bearer '.$token,
        'Accept'        => 'application/vnd.github+json',
    ],
]);

$repo = $api->getRepo('zero-to-prod', 'github-sdk');

$repo->data->full_name;        // 'zero-to-prod/github-sdk'
$repo->data->stargazers_count; // 3

$api->createRepoIssue('zero-to-prod', 'github-sdk', [
    'title' => 'Bug',
    'body'  => 'It broke',
]);

use Zerotoprod\GitHubSdk\Models\CreateRepoIssueRequest;

$api->createRepoIssue('zero-to-prod', 'github-sdk', CreateRepoIssueRequest::from([
    CreateRepoIssueRequest::title => 'Bug',
]));

$api->getRepo('zero-to-prod', 'github-sdk', [
    Options::headers => ['Authorization' => 'Bearer '.$appToken],
]);

$api = new GitHubSdk($config, new CurlHttpTransport(), [
    Hook::before->value => fn (HookContext $ctx) => $ctx->withHeaders([
        'Authorization' => 'Bearer '.$tokens->current(),
    ]),
]);

use Zerotoprod\GitHubSdk\Options;

$api->listRepoIssues('zero-to-prod', 'github-sdk', [
    Options::query => ['state' => 'open', 'labels' => 'bug', 'per_page' => 50],
]);
// GET /repos/zero-to-prod/github-sdk/issues?state=open&labels=bug&per_page=50

$issues = $api->listRepoIssues('zero-to-prod', 'github-sdk', [
    Options::query => ['per_page' => 100, 'page' => 2],
]);

$issues->response->header('Link'); // '<https://api.github.com/...?page=3>; rel="next"'

$result = $api->getRepo('zero-to-prod', 'nope');

$result->failed();                            // true
$result->status();                            // 404
$result->errors->message;                     // 'Not Found'
$result->errors->errors;                      // GitHub's per-field errors, when it sends them
$result->response->header('x-ratelimit-remaining');

$response = $api->getRepo('zero-to-prod', 'github-sdk', [Options::raw => true]);
$response->json('full_name');

use Zerotoprod\GitHubSdk\{GitHubSdk, CurlHttpTransport, Hook, HookContext};

$api = new GitHubSdk($config, new CurlHttpTransport(), [
    Hook::before->value => [
        fn (HookContext $ctx) => $ctx->withHeaders(['X-Trace-Id' => bin2hex(random_bytes(8))]),
        fn (HookContext $ctx) => logger()->debug('Outgoing', $ctx->redacted()),
    ],
    Hook::after->value => [
        fn (HookContext $ctx) => logger()->info("{$ctx->HttpMethod->value} {$ctx->url} → {$ctx->response->status()}"),
    ],
    Hook::onException->value => [
        fn (HookContext $ctx, \Throwable $e) => logger()->error("{$ctx->url} failed: {$e->getMessage()}"),
    ],
]);

$ctx->Hook;       // Hook enum (before / after / onException)
$ctx->HttpMethod; // HttpMethod enum — ->value is 'GET', 'POST', ...
$ctx->url;        // fully qualified request URL
$ctx->options;    // Guzzle-compatible options array (json, headers, query, ...)
$ctx->response;   // transport response during `after`; null otherwise

$ctx->withHeaders(['X-Trace-Id' => $id]); // merge headers — per-call headers survive
$ctx->withOptions(['timeout' => 5]);      // merge options — other options survive
$ctx->redacted();                         // array for logging, credentials masked

use Zerotoprod\GitHubSdk\{RetryingHttpTransport, CurlHttpTransport};

$api = new GitHubSdk($config, new RetryingHttpTransport(new CurlHttpTransport()));

$api = new GitHubSdk($config, new RetryingHttpTransport(
    inner: new CurlHttpTransport(),
    maxAttempts: 5,   // total attempts including the first; default 3, 1 disables retrying
    baseDelay: 0.25,  // seconds the backoff doubles from; default 0.5
    maxDelay: 10.0,   // ceiling for one sleep; default 30.0
));

$api->getRepo('zero-to-prod', 'github-sdk', [
    'timeout'         => 5, // total, seconds (default 30)
    'connect_timeout' => 2, // TCP connect only (default 10)
]);

use Zerotoprod\GitHubSdk\{GitHubSdk, CachingHttpTransport, CurlHttpTransport};
use Illuminate\Support\Facades\Cache;

$api = new GitHubSdk($config, new CachingHttpTransport(
    new CurlHttpTransport(),
    // fn (string $key, Closure $fetch): array — mirrors Cache::remember($key, $ttl, $fetch)
    fn (string $key, \Closure $fetch) => Cache::remember($key, 60, $fetch),
));

$api->getRepo('zero-to-prod', 'github-sdk'); // hits the network; result cached
$api->getRepo('zero-to-prod', 'github-sdk'); // served from cache — no HTTP call

$store = [];

$api = new GitHubSdk($config, new CachingHttpTransport(
    new CurlHttpTransport(),
    function (string $key, \Closure $fetch) use (&$store): array {
        return $store[$key] ??= $fetch();
    },
));

new CachingHttpTransport(
    inner:     new LaravelHttpTransport(),
    cache:     fn (string $key, \Closure $fetch) => Cache::remember($key, 60, $fetch),
    normalize: fn (\Illuminate\Http\Client\Response $r): array => [
        'status' => $r->status(), 'headers' => $r->headers(), 'body' => $r->body(),
    ],
    rehydrate: fn (array $d) => new \Illuminate\Http\Client\Response(
        new \GuzzleHttp\Psr7\Response($d['status'], $d['headers'], $d['body']),
    ),
);

use Zerotoprod\GitHubSdk\Internal\Fake;

new GitHubSdk($config, new CachingHttpTransport(new Fake(), $cache));

// Cache outermost: a hit costs nothing, and only a miss can retry.
new CachingHttpTransport(new RetryingHttpTransport(new CurlHttpTransport()), $cache);

use Zerotoprod\GitHubSdk\{GitHubSdk, GitHubSdkConfig, Response, ApiRoute};

[$api, $fake] = GitHubSdk::fake([
    GitHubSdkConfig::url => 'https://api.github.com',
]);

$fake->queue(
    new Response(200, [], json_encode(['full_name' => 'zero-to-prod/github-sdk'])),
    new Response(404, [], json_encode(['message' => 'Not Found'])),
);

$found   = $api->getRepo('zero-to-prod', 'github-sdk');
$missing = $api->getRepo('zero-to-prod', 'nope');

$fake->assertSent('GET', '/repos/zero-to-prod/github-sdk'); // method + URL substring
$fake->assertNotSent('DELETE');
$fake->assertSentCount(2);

$fake->recorded()[0]['method']; // 'GET'
$fake->recorded()[0]['url'];    // 'https://api.github.com/repos/zero-to-prod/github-sdk'

ApiRoute::repo->value;                                // '/repos/{owner}/{repo}'
ApiRoute::repo_issues->value;                         // '/repos/{owner}/{repo}/issues'
ApiRoute::repo_issues(['per_page' => 50])->render();  // '/repos/{owner}/{repo}/issues?per_page=50'

use Illuminate\Support\Facades\Http;
use Zerotoprod\GitHubSdk\{GitHubSdk, LaravelHttpTransport, ApiRoute};

Http::fake([
    '*/repos/zero-to-prod/github-sdk' => Http::response(['full_name' => 'zero-to-prod/github-sdk'], 200),
]);

$response = (new GitHubSdk([], new LaravelHttpTransport()))->getRepo('zero-to-prod', 'github-sdk');

self::assertTrue($response->ok());
Http::assertSent(fn ($r) => str_ends_with($r->url(), '/repos/zero-to-prod/github-sdk'));

use Zerotoprod\GitHubSdk\Factories\{ErrorsFactory, GitHubSdkConfigFactory};
use Zerotoprod\GitHubSdk\Models\Errors;

[$api, $fake] = GitHubSdk::fake(GitHubSdkConfigFactory::factory()->context());

$fake->queue(new Response(404, [], ErrorsFactory::factory()
    ->set(Errors::message, 'Not Found')
    ->json()));

$issues = $api->listRepoIssues('zero-to-prod', 'github-sdk');

foreach ($issues->data as $issue) {
    $issue->title;
    $issue->user->login;
}

GitHubSdkConfig::model_namespace => 'App\\Models\\GitHub',

// app/Models/GitHub/Repository.php (published)
namespace App\Models\GitHub;

use Zerotoprod\GitHubSdk\Internal\DataModel;

class Repository
{
    use DataModel;
    // ... generated properties ...

    public function isActive(): bool
    {
        return $this->archived === false && $this->disabled === false;
    }
}

use Zerotoprod\GitHubSdk\HttpTransport;

/** @implements HttpTransport<\Psr\Http\Message\ResponseInterface> */
class GuzzleTransport implements HttpTransport
{
    public function __construct(private \GuzzleHttp\ClientInterface $client) {}

    public function request(string $method, string $url, array $options = []): \Psr\Http\Message\ResponseInterface
    {
        return $this->client->request($method, $url, $options);
    }
}

$api = new GitHubSdk($config, new GuzzleTransport($client));