Using Github Webhooks with PHP

Younes Rafie
Share

In the first part of our series, we talked about the Github API and built a demo in the process. In this part we are going to explore the Webhooks API, and we will build a demo to showcase the API usage. Let’s get started.

Github logo

What We’re Building

In this article we are going to set up a demo that receives every push from Github and saves it to a database. We are also going to create a page to display the list of our repository committers ordered by the number of commits. You can check the final result on Github.

Setting Up the Environment

We will be using Laravel 5 as our framework, and because our application must be accessible from the internet to work with Github Webhooks, we can use Heroku to host our code. You can check this post to get started with Heroku.

Configuration

Adding Mysql

After creating our app on Heroku, we need to add MySQL to store the received data.

heroku addons:add cleardb

You can go to your dashboard to get your database connection credentials. Set the necessary variables inside your config as you can see in the example below.

MYSQL_HOST=‘HOST’
MYSQL_DATABASE=‘DB’
MYSQL_USERNAME=‘USERNAME’
MYSQL_PASSWORD=‘PASSWORD’

Register Github Hooks

To register a new hook, navigate to a repository inside your account, then go to the settings tab and select Webhooks & Services.

Register Webhook

After setting your application URL endpoint to receive the payload, you can set a secret key to be sent along with the payload to verify the request origin. By default, Github will send you only push events, but you can select individual events from the list. For our demo, the push event is sufficient.

Building a Demo

Creating The Database Tables

php artisan make:migration create_hooks_table
// database/migrations/XXX_create_hooks_table.php

class CreateHooksTable extends Migration
{

  public function up()
  {
    Schema::create('hooks', function (Blueprint $table) {
      $table->increments('id');
      $table->string('event_name', 100);
      //you can separate the sender, repo...etc. but let's just keep it simple
      $table->text('payload');
      $table->timestamps();
    });
  }

  public function down()
  {
    Schema::drop('hooks');
  }

}

We only need a hooks table to store the incoming requests. Registering the event_name will help us in the future if we plan to track other events, and it will make it easier for us to look up the database.
The payload field holds the request body. You can split this field into multiple parts and create a sender, a repository, a list of commits, etc.

After running the migration using php artisan migrate, we will create our Hook model class.

// app/Hook.php

class Hook extends Model
{

  protected $table = ‘hooks’;

}

Creating Routes

Our demo application will only have two routes.

  • an events route, which will receive the Github event payload.
  • a /report/contributions route, which will display a chart of project contributors.
// app/Http/routes.php

Route::post(/events’, [‘uses’ => ‘GithubController@storeEvents’]);
// app/Http/Controllers/GithubController.php

public function storeEvents(Request $request) {
  $event_name = $request->header(X-Github-Event’);
  $body = json_encode(Input::all());

  $hook = new Hook;
  $hook->event_name = $event_name;
  $hook->payload = $body;

  $hook->save();

  return ‘’;// 200 OK
}

The X-Github-Event header value contains the event name that occurred. In this case, it will be equal to push. If you want to test the request origin using the secret key you provided previously on Github, you can use the X-Hub-Signature header value. You can read more in the documentation.

Github will send you an almost real time request. If you are using Heroku for deployment, you can use the logger to monitor requests while testing. You can read more about that in the documentation. You can also navigate to your Webhook configuration on Github and scroll to the Recent Deliveries tab at the bottom.

heroku logs —tail -n 0 —ps router

Test push

To display the list of contributors I’m going to use the ChartJs JavaScript library. You can check out this getting started guide for a good intro.

// app/Http/routes.php

Route::get(/reports/contributions.json’, [‘uses’ => ‘GithubController@contributionsJson’]);
// app/Http/Controllers/GithubController.php
public function contributionsJson()
{
  $hooks = Hook::where('event_name', '=', 'push')->get(['payload']);

  $users = [];
  $hooks->each(function ($item) use (&$users) {
    $item = json_decode($item['payload']);

    $pusherName = $item->pusher->name;
    $commitsCount = count($item->commits);

    $users[$pusherName] = array_pull($users, $pusherName, 0) + $commitsCount;
  });

  return [
      'users'   => array_keys($users),
      'commits' => array_values($users)
  ];
}

The /reports/contributions.json route will retrieve the list of push events from our database and loop through it to retrieve the list of users by commits. We then return the list of users and commits separately to suit the library needs. However, you can also choose to do it on the front-end using some Javascript.

// app/Http/routes.php

Route::get(/reports/contributions’, [‘uses’ => ‘GithubController@contributions’]);
// app//Http/Contrllers/GithubController.php

public function contributions()
{
  
  return View::make('reports.contributions');
}
// resources/views/reports/contributions.blade.php

<canvas id=“contributions” width=400” height=400></canvas>

<script>
    var ctx = document.getElementById(“contributions”).getContext(2d”);

    $.ajax({
        url:/reports/contributions.json”,
        dataType: “json”,
        type:GET,
        success: function(response){
            var data = {
                labels: response.users,
                datasets: [
                {
                    data: response.commits
                }]
            };

            new Chart(ctx).Bar(data, {});
        }
    });
</script>

After getting the data from our JSON endpoint, we create a new Chart object. You can read more about the available options in the documentation.

Contributors chart

Conclusion

Here’s a real world example – if you’ve ever contributed to a Symfony project on Github, you’ll have noticed that when you send a pull request, your code is being checked by an external service called fabbot.io. This service is watching the repository for every new pull request and does some checks on your code. You can visit the website for more info, and you can also register your own repository to be checked.

The Github API has a lot to offer, and the documentation makes it really easy to get started. I do recommend you check it out. Additionally, you can check the final result on Github. And like always, if you have any questions, don’t hesitate to post them below.