Skip to content

Instantly share code, notes, and snippets.

@shgysk8zer0
Last active August 29, 2015 14:10
Show Gist options
  • Save shgysk8zer0/6e80988945ed5213288b to your computer and use it in GitHub Desktop.
Save shgysk8zer0/6e80988945ed5213288b to your computer and use it in GitHub Desktop.
PHP class and usage for GitHub Webooks
{
"secret": "Secret set when creating Web Hook"
}
<?php
/**
* Handle Webhooks from GitHub with ease!
*
* @author Chris Zuber <shgysk8zer0@gmail.com>
* @copyright 2014, Chris Zuber
*
* @var array $headers [The headers sent]
* @var string $payload [The body]
* @var string $event [Event declared in X-Github-Event header]
* @var \stdClass $parsed [$payload parsed as an Object]
* @var \stdClass $config [Configuration data parsed as an Object]
*
* @link https://developer.github.com/webhooks/
*
* @license http://opensource.org/licenses/GPL-3.0 GNU General Public License, version 3 (GPL-3.0)
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace core;
class GitHubWebhook {
public $headers = [], $payload = null, $event = null, $parsed = null;
protected $config = null;
/**
* Construct the class & set its variables
*
* @param mixed $config [Configuration data, possibly an .ini or .json]
*/
public function __construct($config = null) {
$this->headers = getallheaders();
if(array_key_exists('X-Github-Event', $this->headers)) {
$this->event = $this->headers['X-Github-Event'];
}
if(isset($config)) {
$this->parseConfig($config);
}
$this->parsePayload();
}
/**
* Validate Content-Length, User-Agent, and X-Hub-Signature
*
* @link https://github.com/github/github-services/blob/f3bb3dd780feb6318c42b2db064ed6d481b70a1f/lib/service/http_helper.rb#L77
*
* @param string $secret [Secret key set when creating the Webhook]
* @return bool
*/
public function validate($secret = null) {
if(
is_null($secret)
and is_object($this->config)
and @is_string($this->config->secret)
) {
$secret = $this->config->secret;
}
if(
array_key_exists('Content-Length', $this->headers)
and array_key_exists('User-Agent', $this->headers)
and preg_match('/^GitHub-Hookshot/', $this->headers['User-Agent'])
and array_key_exists('X-Hub-Signature', $this->headers)
) {
if(is_string($secret)) {
list($algo, $hash) = explode('=', $this->headers['X-Hub-Signature'], 2);
return $hash === hash_hmac(
$algo,
$this->payload,
$secret
);
}
else {
return true;
}
}
else {
return false;
}
}
/**
* Parse $config, whether it be an Object or a file
*
* @param mixed $config [Configuration data, possibly an .ini or .json]
* @return void
*/
private function parseConfig($config) {
if(is_string($config) and @file_exists($config) and @is_readable($config)) {
switch(strtolower(pathinfo($config, PATHINFO_EXTENSION))) {
case 'json': {
$this->config = json_decode(file_get_contents($config));
} break;
case 'ini': {
$this->config = (object)parse_ini_file($config);
} break;
}
}
elseif(is_string($config)) {
$this->config = new \stdClass();
$this->config->secret = $config;
}
elseif(is_array($config)) {
$this->config = (object)$config;
}
elseif(is_object($config)) {
$this->config = $config;
}
}
/**
* Parses data from $payload into an object
* @todo Make work with different content-type (form-data)
*
* @param void
* @return void
*/
private function parsePayload() {
if(array_key_exists('content-type', $this->headers)) {
switch(strtolower($this->headers['content-type'])) {
case 'application/json': {
$this->payload = file_get_contents('php://input');
if(strlen($this->payload) === (int)$this->headers['Content-Length']) {
$this->parsed = json_decode($this->payload);
}
} break;
}
}
}
}
?>
<?php
//Disable Error Reporting
error_reporting(0);
//Add classes/ directory to include_path
set_include_path(__DIR__ . DIRECTORY_SEPARATOR . 'classes' . PATH_SEPARATOR . get_include_path());
//Load classes with the extension ".class.php"
spl_autoload_extensions('.class.php');
//Enable the autoloader
spl_autoload_register();
//Class is located in classes/core/githubwebhook.class.php
//Load the class
$webhook = new \core\GitHubWebhook('config/github.json');
//Check that correct headers are set, and verify the X-Hub-Signature header
if($webhook->validate()) {
//Append to file
file_put_contents('hook.json', json_encode($webhook->parsed, JSON_PRETTY_PRINT), FILE_APPEND | LOCK_EX);
}
else {
//If not valid, exit with a 404
http_response_code(404);
exit();
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment