PHP & MySQL, Tutorials, Tools

For anyone that uses Github know’s how useful it is, especially on the collaboration front. Allowing other people to work on your projects, for instance for Nova Framework I have a Docs repo. When I update the docs it would be really good if the website docs could be updated automatically. 

This can be achieved by using a Github Webhook

Webhooks allow external services to be notified when certain events happen within your repository. When the specified events happen, we’ll send a POST request to each of the URLs you provide. Learn more in our Webhooks Guide.

In this tutorial I will cover how to setup a hook and do something with it on a remote website using PHP. First create a new webhook by going to your repository on Github and click on Settings then Webhooks then Add Webhook.

You will see a screen like this:

The Payload URL is the address to your script ie http://domain.com/github-listener  

The content type is either Json or x-www-form-urlencoded I will be using Json

The last option is optional but is recommended to enter is a unique pass phrase. http://randomkeygen.com/ is a good site for getting a random key. The secret is passed to the url as part of the signature, this means you have a way to validate the request has indeed come from Github and not some other url.

The next section you specify if you want just the push event or to send everything. The last option gives a full list of events that can be picked from. In this case I only want the push event.

Once you’ve filled in the fields click on Add webhook and you’re all set now every time a commit is made to that repo a payload will be posted to your website. You can see past payloads by clicking on the webhook and looking to a Recent Deliveries section list will be recent payloads.

Clicking on one will reveal the headers sent:

Request URL: http://domain.com/githup-payload
Request method: POST
content-type: application/json
Expect:
User-Agent: GitHub-Hookshot/373c228
X-GitHub-Delivery: b1fe4300-8d41-11e6-80ce-4beed135db19
X-GitHub-Event: push
X-Hub-Signature: sha1=548d285225f7787073145ebf68e56339e0b46bcd

Then a payload underneath. Also there is a Response tab that reveals the response from your website, that’s really useful when debugging.

Process payloads on your website

To listen for payloads monitor for new input. Tying to $_POST will fail outright.

$body = file_get_contents('php://input');

Once you have the payload you’ll need to decode the json, pass true if you want to work with an array. By default json_decode will return an object.

$payload = json_decode($body, true);

From there it’s up to you what you do with the information. For instance in my case I only want to know which files have been edited I can do that by looking through the commits and modified:

foreach($payload['commits'][0]['modified'] as $filepath){

	switch ($filepath) {
		case 'basics/cli-console.md':
			$id = 40;
			break;
		//more
		default:
			return;
			break;
	}
	
	$path = 'https://raw.githubusercontent.com/nova-framework/docs/3.0/'.$filepath;
	$body = file_get_contents($path);
	
	$data = ['postCont' => $body];
	$where = ['postID' => $id];

	$db = \Helpers\Database::get();
	$db->update(PREFIX.'docsv3_posts', $data, $where);

}

To ensure the data is coming from Github and not some other place the secret can be used thanks to Craig Blanchette for this snippet: replace secret with your own secret set in Github.

$secret = 'secret';
$headers = getallheaders();
$hubSignature = $headers['X-Hub-Signature'];
// Split signature into algorithm and hash
list($algo, $hash) = explode('=', $hubSignature, 2);
// Get payload
$payload = file_get_contents('php://input');
// Calculate hash based on payload and the secret
$payloadHash = \hash_hmac($algo, $payload, $secret);
// Check if hashes are equivalent
if ($hash !== $payloadHash) { // Kill the script or do something else here. die;
}

Putting it all together:

$secret = 'secret';
$headers = getallheaders();
$hubSignature = $headers['X-Hub-Signature'];
// Split signature into algorithm and hash
list($algo, $hash) = explode('=', $hubSignature, 2);
// Get payload
$payload = file_get_contents('php://input');
// Calculate hash based on payload and the secret
$payloadHash = \hash_hmac($algo, $payload, $secret);
// Check if hashes are equivalent
if ($hash !== $payloadHash) { // Kill the script or do something else here. die;
}
//get payload it exists
$body = file_get_contents('php://input');
//decode json
$payload = json_decode($body, true);