├── .gitignore ├── .htaccess ├── README.md ├── composer.json ├── config ├── chatwork.json.default └── slack.json.default ├── gateway.php ├── index.html ├── lib ├── BitbucketEventNotification │ ├── Api │ │ ├── BaseApiClient.php │ │ ├── ChatworkApiClient.php │ │ └── SlackApiClient.php │ ├── Config │ │ └── ConfigLoader.php │ ├── DestinationService │ │ ├── ChatworkService.php │ │ ├── DestinationService.php │ │ └── SlackService.php │ ├── JsonParser │ │ └── JsonParser.php │ ├── Log │ │ └── MLog.php │ └── PullRequest │ │ ├── PullRequest.php │ │ ├── PullRequestCommentCreated.php │ │ ├── PullRequestCreated.php │ │ ├── PullRequestDeclined.php │ │ ├── PullRequestMerged.php │ │ └── PullRequestUpdated.php ├── SplClassLoader.php ├── autoload.php └── utils.php ├── robots.txt ├── sample-merged-hook-request.json ├── tmp ├── cache │ └── empty ├── empty └── logs │ └── empty └── vendor └── empty /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | tmp/* 4 | vendor/* 5 | composer.lock 6 | config/chatwork.json 7 | config/slack.json 8 | -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | Options -Indexes 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | BitbucketEventNotification 2 | ========================== 3 | 4 | Notify Chatwork or Slack, when receive pull request notifications from Bitbucket. 5 | 6 | ![appearance of chat room](http://sousaku-memo.net/wp-content/uploads/2014/11/appearance-of-chat-room.png) 7 | 8 | ## Notification Flow 9 | 10 | 1. Pull Request on Bitbucket 11 | 2. Hook post request to your server will occur. 12 | 3. This application receives the request, and post notification messages to the chat. (Chatwork or Slack) 13 | 14 | ## Supported Pull Request Notification 15 | 16 | - Created 17 | - Merged 18 | - Updated 19 | - Declined 20 | - Comment created (on the pull request page) 21 | 22 | ## Requires 23 | 24 | * PHP 5.3+ with cURL with composer 25 | * Access token of Chatwork, if you want notification to that. 26 | * Access token of Slack, if you want notification to that. (xoxp-xxxxx) 27 | * If you want to create a token to access to the page of [Slack API](https://api.slack.com/). 28 | * Bitbucket repository with administrator right. 29 | 30 | ## Installation 31 | 32 | 1. Get source code from GitHub, either using Git, or by downloading directly. 33 | 2. Copy the source files to public directory in your server. 34 | 3. Setup correct permissions 35 | * `chmod -R 777 tmp` 36 | 4. Adjust token in your config file. 37 | * for the Chatwork 38 | * `cp config/chatwork.json.default config/chatwork.json` 39 | * `vim config/chatwork.json` 40 | * for the Slack 41 | * `cp config/slack.json.default config/slack.json` 42 | * `vim config/slack.json` 43 | 5. Plugin install with composer. (You need to install composer.) 44 | * `composer install` 45 | 6. Please set the following post destination url in chat work your account setting page. (room_id is chatwork room id.) 46 | * Sample pull request post hook url (for Chatwork): 47 | * http://example.com/bitbucket_event_notification/gateway.php?destination_service=chatwork&room_id=1000000000 48 | * Sample pull request post hook url (for Slack): 49 | * http://example.com/bitbucket_event_notification/gateway.php?destination_service=slack&room_id=C1234567890 50 | 51 | ## Parameters of gateway.php (GET) 52 | 53 | |Key|Description|Example for Chatwork|Example for Slack| 54 | |:---|:---|:---|:---| 55 | |destination_service|Post destination service name.|chatwork|slack| 56 | |room_id|Post destination.|1000000000|#bitbucket, C1234567890| 57 | 58 | * If you include the # to the channel name, please URL-encoded. 59 | * Example: `#bitbucket` -> `%23bitbucket` 60 | 61 | ## Author 62 | 63 | - [創作メモ帳](http://sousaku-memo.net/) 64 | - [創作メモ帳 - Slackへのプルリクエスト通知に対応しました](http://sousaku-memo.net/php-system/1264) : Japanese document is here 65 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "monolog/monolog": ">=1.0.0" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /config/chatwork.json.default: -------------------------------------------------------------------------------- 1 | { 2 | "token": "set your token" 3 | } -------------------------------------------------------------------------------- /config/slack.json.default: -------------------------------------------------------------------------------- 1 | { 2 | "token": "set your token (xoxp-xxxxxxx)" 3 | } -------------------------------------------------------------------------------- /gateway.php: -------------------------------------------------------------------------------- 1 | info('Access from ip address: ' . $_SERVER['REMOTE_ADDR']); 31 | 32 | header('Content-type: application/json; charset=utf-8'); 33 | 34 | // Check arguments 35 | if (!isset($_GET['room_id']) || !$_GET['room_id']) { 36 | MLog::getInstance()->err('Invalid room id parameter.'); 37 | header('HTTP', true, 403); 38 | echo json_encode(array('result' => false, 'message' => 'Invalid access.')); 39 | exit; 40 | } 41 | 42 | if (!isset($_GET['destination_service']) || !$_GET['destination_service']) { 43 | // default service: chatwork (keep backward compatibility) 44 | $_GET['destination_service'] = 'chatwork'; 45 | } 46 | 47 | // Get json from chatwork 48 | $inputPath = USE_TEST_JSON ? APP_DIR . '/sample-merged-hook-request.json' : 'php://input'; 49 | $rawJson = file_get_contents($inputPath); 50 | MLog::getInstance()->info('Request json: ' . json_encode(json_decode($rawJson))); 51 | 52 | // Parse json 53 | $parser = new JsonParser(); 54 | $jsonData = $parser->parse($rawJson); 55 | $pullRequest = PullRequest::create($jsonData); 56 | 57 | MLog::getInstance()->info("Pull request type is " . get_class($pullRequest)); 58 | 59 | if ($pullRequest === null) { 60 | MLog::getInstance()->err('Failed to parse json data. An unsupported json format.'); 61 | if (!empty($jsonData)) { 62 | MLog::getInstance()->err('json data:' . print_r($jsonData, true)); 63 | } 64 | header('HTTP', true, 403); 65 | echo json_encode(array('result' => false, 'message' => 'Failed to parse data.')); 66 | exit; 67 | } 68 | 69 | // Decide destination service 70 | $destinationService = DestinationService::create($_GET['destination_service'], $pullRequest); 71 | MLog::getInstance()->info("Destination service type is " . get_class($destinationService)); 72 | if (!$destinationService) { 73 | MLog::getInstance()->err('Invalid destination service parameter.'); 74 | MLog::getInstance()->err('Specified destination service value:' . $_GET['destination_service']); 75 | header('HTTP', true, 403); 76 | echo json_encode(array('result' => false, 'message' => 'Invalid access.')); 77 | exit; 78 | } 79 | 80 | // Post message 81 | $response = $destinationService->postMessage(array('room_id' => $_GET['room_id'])); 82 | 83 | // Response 84 | if ($response !== null) { 85 | MLog::getInstance()->info('Return success response.'); 86 | header('HTTP', true, 200); 87 | echo json_encode($response); 88 | exit; 89 | } else { 90 | MLog::getInstance()->err('Return error response.'); 91 | header('HTTP', true, 403); 92 | echo json_encode(array('result' => false, 'message' => 'Failed to post message.')); 93 | exit; 94 | } 95 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ymm1x/BitbucketEventNotification/181a29ccffdbb4fbb4b836fc431d543670416c14/index.html -------------------------------------------------------------------------------- /lib/BitbucketEventNotification/Api/BaseApiClient.php: -------------------------------------------------------------------------------- 1 | $message); 31 | 32 | $ch = curl_init(); 33 | curl_setopt($ch, CURLOPT_URL, self::BASE_API_URL . 'v1/rooms/' . intval($roomId) . '/messages'); 34 | curl_setopt($ch, CURLOPT_HTTPHEADER, array('X-ChatWorkToken: ' . $this->loadAccessToken())); 35 | curl_setopt($ch, CURLOPT_POST, 1); 36 | curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($option, '', '&')); 37 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 38 | 39 | $response = curl_exec($ch); 40 | $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); 41 | if ($statusCode === 200) { 42 | if ($response = json_decode($response)) { 43 | MLog::getInstance()->info("Successful response data:" . json_encode($response)); 44 | return $response; 45 | } 46 | } 47 | 48 | MLog::getInstance()->err("Error occurred while executing chatwork api."); 49 | MLog::getInstance()->err("Status code: {$statusCode}"); 50 | MLog::getInstance()->err("Response: " . json_encode($response)); 51 | 52 | return null; 53 | } 54 | 55 | /** 56 | * Get access token from config file. 57 | * 58 | * @return mixed returns config value. false is returned if the failed for load. 59 | */ 60 | public function loadAccessToken() 61 | { 62 | $loader = new ConfigLoader(__DIR__ . '/../../../config'); 63 | return $loader->load('chatwork', 'token'); 64 | } 65 | } -------------------------------------------------------------------------------- /lib/BitbucketEventNotification/Api/SlackApiClient.php: -------------------------------------------------------------------------------- 1 | $this->loadAccessToken(), 33 | 'channel' => $channel, 34 | 'text' => $message, 35 | 'username' => 'Bitbucket', 36 | 'icon_url' => 'https://slack.global.ssl.fastly.net/20653/img/services/bitbucket_48.png', 37 | 'attachments' => json_encode($attachments), 38 | ); 39 | 40 | $ch = curl_init(); 41 | curl_setopt($ch, CURLOPT_URL, self::BASE_API_URL . 'chat.postMessage'); 42 | curl_setopt($ch, CURLOPT_POST, 1); 43 | curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($option, '', '&')); 44 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 45 | 46 | $response = curl_exec($ch); 47 | $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); 48 | if ($statusCode === 200 && $response = json_decode($response)) { 49 | if ($response->ok === true) { 50 | MLog::getInstance()->info("Successful response data:" . json_encode($response)); 51 | return $response; 52 | } 53 | } 54 | 55 | MLog::getInstance()->err("Error occurred while executing slack api."); 56 | MLog::getInstance()->err("Status code: {$statusCode}"); 57 | MLog::getInstance()->err("Response: " . json_encode($response)); 58 | 59 | return null; 60 | } 61 | 62 | /** 63 | * Get access token from config file. 64 | * 65 | * @return mixed returns config value. false is returned if the failed for load. 66 | */ 67 | public function loadAccessToken() 68 | { 69 | $loader = new ConfigLoader(__DIR__ . '/../../../config'); 70 | return $loader->load('slack', 'token'); 71 | } 72 | } -------------------------------------------------------------------------------- /lib/BitbucketEventNotification/Config/ConfigLoader.php: -------------------------------------------------------------------------------- 1 | configDir = $configDir; 21 | } 22 | 23 | /** 24 | * Load config value of config json file. 25 | * 26 | * @param $configName config json file name 27 | * @param $key key 28 | * @return mixed returns config value. false is returned if the failed for load. 29 | */ 30 | public function load($configName, $key) 31 | { 32 | $configJson = file_get_contents($this->configDir . '/' . $configName . '.json'); 33 | if (!$configJson) { 34 | return false; 35 | } 36 | $config = json_decode($configJson, true); 37 | if (!array_key_exists($key, $config)) { 38 | return false; 39 | } 40 | return $config[$key]; 41 | } 42 | } -------------------------------------------------------------------------------- /lib/BitbucketEventNotification/DestinationService/ChatworkService.php: -------------------------------------------------------------------------------- 1 | getNotifyMessage(); 40 | 41 | /** 42 | * @var ChatworkApiClient $apiClient 43 | */ 44 | $apiClient = $this->getApiClient(); 45 | $response = $apiClient->postMessage($extendedParams['room_id'], $notifyMessage); 46 | 47 | return $response; 48 | } 49 | 50 | /** 51 | * Get notify message string for post. 52 | * 53 | * @return string|null null if failed 54 | */ 55 | private function getNotifyMessage() 56 | { 57 | $data = $this->pullRequest->getData(); 58 | 59 | $notify = ''; 60 | if ($this->pullRequest instanceof PullRequestCreated) { 61 | $notify .= sprintf("[info]"); 62 | $notify .= sprintf("[title]Pull request has been created by %s. Please review(bow)[/title]", $data['author']['display_name']); 63 | $notify .= sprintf("[CREATED] #%d %s", $data['id'], $data['title']); 64 | if (strlen($data['description']) > 0) { 65 | $notify .= sprintf("\n%s", $data['description']); 66 | } 67 | $notify .= sprintf("\nhttps://bitbucket.org/%s/pull-request/%d", $data['destination']['repository']['full_name'], $data['id']); 68 | $notify .= sprintf("[/info]"); 69 | } else if ($this->pullRequest instanceof PullRequestDeclined) { 70 | $notify .= sprintf("[info]"); 71 | $notify .= sprintf("[title]Pull request has been declined by %s:([/title]", $data['author']['display_name']); 72 | $notify .= sprintf("[DECLINED] %s", $data['title']); 73 | $notify .= sprintf("[/info]"); 74 | } else if ($this->pullRequest instanceof PullRequestCommentCreated) { 75 | $notify .= sprintf("[info]"); 76 | $notify .= sprintf("[title]Comment was posted by %s(*)[/title]", $data['user']['display_name']); 77 | $notify .= sprintf("%s", $data['content']['raw']); 78 | $notify .= sprintf("\n%s", PullRequest::replaceUrlForLink($data['links']['html']['href'])); 79 | $notify .= sprintf("[/info]"); 80 | } else if ($this->pullRequest instanceof PullRequestMerged) { 81 | $notify .= sprintf("[info]"); 82 | $notify .= sprintf("[title]Pull request has been merged by %s. Good job8-)[/title]", $data['author']['display_name']); 83 | $notify .= sprintf("[MERGED] %s", $data['title']); 84 | $notify .= sprintf("[/info]"); 85 | } else if ($this->pullRequest instanceof PullRequestUpdated) { 86 | $notify .= sprintf("[info]"); 87 | $notify .= sprintf("[title]Pull request has been updated by %s. Please re-review:p[/title]", $data['author']['display_name']); 88 | $notify .= sprintf("[UPDATED] %s", $data['title']); 89 | $notify .= sprintf("[/info]"); 90 | } else { 91 | $notify = null; 92 | } 93 | return $notify; 94 | } 95 | } -------------------------------------------------------------------------------- /lib/BitbucketEventNotification/DestinationService/DestinationService.php: -------------------------------------------------------------------------------- 1 | serviceName = $serviceName; 30 | $this->pullRequest = $pullRequest; 31 | } 32 | 33 | /** 34 | * @return BaseAPIClient get api client of this service. 35 | */ 36 | abstract function getApiClient(); 37 | 38 | /** 39 | * @param array $extendedParams optional 40 | * @return mixed returns the response array if api successful. 41 | * otherwise, returns null. 42 | */ 43 | abstract function postMessage($extendedParams = array()); 44 | 45 | /** 46 | * Return the destination service instance by service name. 47 | * 48 | * @param string $serviceName 49 | * @param PullRequest $pullRequest 50 | * @return DestinationService 51 | */ 52 | public static function create($serviceName, $pullRequest) 53 | { 54 | $serviceName = strtolower(trim($serviceName)); 55 | 56 | $instance = null; 57 | switch ($serviceName) { 58 | case 'chatwork': 59 | $instance = new ChatworkService($pullRequest); 60 | break; 61 | case 'slack': 62 | $instance = new SlackService($pullRequest); 63 | break; 64 | default: 65 | break; 66 | } 67 | 68 | return $instance; 69 | } 70 | } -------------------------------------------------------------------------------- /lib/BitbucketEventNotification/DestinationService/SlackService.php: -------------------------------------------------------------------------------- 1 | getNotifyMessage(); 33 | $attachments = $this->getAttachments(); 34 | 35 | /** 36 | * @var SlackApiClient $apiClient 37 | */ 38 | $apiClient = $this->getApiClient(); 39 | $response = $apiClient->postMessage($extendedParams['room_id'], $notifyMessage, $attachments); 40 | 41 | return $response; 42 | } 43 | 44 | /** 45 | * @inheritdoc 46 | */ 47 | public function getApiClient() 48 | { 49 | return new SlackApiClient(); 50 | } 51 | 52 | /** 53 | * Get notify message string for post. 54 | * 55 | * @return string|null null if failed 56 | */ 57 | private function getNotifyMessage() 58 | { 59 | $data = $this->pullRequest->getData(); 60 | 61 | $notify = ''; 62 | if ($this->pullRequest instanceof PullRequestCreated) { 63 | $notify .= sprintf("Pull request has been created by %s. Please review:bow:", $data['author']['display_name']); 64 | $notify .= sprintf("\nhttps://bitbucket.org/%s/pull-request/%d", $data['destination']['repository']['full_name'], $data['id']); 65 | } else if ($this->pullRequest instanceof PullRequestDeclined) { 66 | $notify .= sprintf("Pull request has been declined by %s:disappointed:", $data['author']['display_name']); 67 | } else if ($this->pullRequest instanceof PullRequestCommentCreated) { 68 | $notify .= sprintf("Comment was posted by %s:star:", $data['user']['display_name']); 69 | $notify .= sprintf("\n%s", PullRequest::replaceUrlForLink($data['links']['html']['href'])); 70 | } else if ($this->pullRequest instanceof PullRequestMerged) { 71 | $notify .= sprintf("Pull request has been merged by %s. Good job:sunglasses:", $data['author']['display_name']); 72 | } else if ($this->pullRequest instanceof PullRequestUpdated) { 73 | $notify .= sprintf("Pull request has been updated by %s. Please re-review:stuck_out_tongue:", $data['author']['display_name']); 74 | } else { 75 | $notify = null; 76 | } 77 | return $notify; 78 | } 79 | 80 | 81 | /** 82 | * Get attachments data. 83 | * 84 | * @return array 85 | */ 86 | private function getAttachments() 87 | { 88 | $data = $this->pullRequest->getData(); 89 | 90 | $fields = array(); 91 | 92 | if ($this->pullRequest instanceof PullRequestCreated) { 93 | $fields[] = array( 94 | 'title' => 'Author', 95 | 'value' => $data['author']['display_name'], 96 | 'short' => true, 97 | ); 98 | $fields[] = array( 99 | 'title' => 'Title', 100 | 'value' => sprintf("#%d %s", $data['id'], $data['title']), 101 | 'short' => true, 102 | ); 103 | $fields[] = array( 104 | 'title' => 'Source', 105 | 'value' => $data['source']['branch']['name'], 106 | 'short' => true, 107 | ); 108 | $fields[] = array( 109 | 'title' => 'Destination', 110 | 'value' => $data['destination']['branch']['name'], 111 | 'short' => true, 112 | ); 113 | if (strlen($data['description']) > 0) { 114 | $fields[] = array( 115 | 'title' => 'Description', 116 | 'value' => sprintf("\n%s", $data['description']), 117 | 'short' => false, 118 | ); 119 | } 120 | } else if ($this->pullRequest instanceof PullRequestDeclined) { 121 | $fields[] = array( 122 | 'title' => 'Author', 123 | 'value' => $data['author']['display_name'], 124 | 'short' => true, 125 | ); 126 | $fields[] = array( 127 | 'title' => 'Title', 128 | 'value' => sprintf("%s", $data['title']), 129 | 'short' => true, 130 | ); 131 | } else if ($this->pullRequest instanceof PullRequestCommentCreated) { 132 | $fields[] = array( 133 | 'title' => 'Author', 134 | 'value' => $data['user']['display_name'], 135 | 'short' => true, 136 | ); 137 | $fields[] = array( 138 | 'title' => 'Content', 139 | 'value' => sprintf("%s", $data['content']['raw']), 140 | 'short' => true, 141 | ); 142 | } else if ($this->pullRequest instanceof PullRequestMerged) { 143 | $fields[] = array( 144 | 'title' => 'Author', 145 | 'value' => $data['author']['display_name'], 146 | 'short' => true, 147 | ); 148 | $fields[] = array( 149 | 'title' => 'Title', 150 | 'value' => sprintf("%s", $data['title']), 151 | 'short' => true, 152 | ); 153 | } else if ($this->pullRequest instanceof PullRequestUpdated) { 154 | $fields[] = array( 155 | 'title' => 'Author', 156 | 'value' => $data['author']['display_name'], 157 | 'short' => true, 158 | ); 159 | $fields[] = array( 160 | 'title' => 'Title', 161 | 'value' => sprintf("%s", $data['title']), 162 | 'short' => true, 163 | ); 164 | } else { 165 | $notify = null; 166 | } 167 | 168 | $attachments = array( 169 | array( 170 | 'fields' => $fields 171 | ) 172 | ); 173 | 174 | return $attachments; 175 | } 176 | 177 | } -------------------------------------------------------------------------------- /lib/BitbucketEventNotification/JsonParser/JsonParser.php: -------------------------------------------------------------------------------- 1 | pushHandler(new StreamHandler($loggingPath, $level)); 55 | } 56 | } -------------------------------------------------------------------------------- /lib/BitbucketEventNotification/PullRequest/PullRequest.php: -------------------------------------------------------------------------------- 1 | setData($jsonData); 59 | } 60 | 61 | return $instance; 62 | } 63 | 64 | /** 65 | * @param array $data 66 | */ 67 | public function setData($data) 68 | { 69 | $this->data = $data; 70 | } 71 | 72 | /** 73 | * @return array 74 | */ 75 | public function getData() 76 | { 77 | return $this->data; 78 | } 79 | 80 | /** 81 | * Replace endpoint url to url link. 82 | * 83 | * @param string $url 84 | * @return string 85 | */ 86 | public static function replaceUrlForLink($url) 87 | { 88 | return str_replace('://api.', '://', $url); 89 | } 90 | } -------------------------------------------------------------------------------- /lib/BitbucketEventNotification/PullRequest/PullRequestCommentCreated.php: -------------------------------------------------------------------------------- 1 | . 19 | */ 20 | 21 | /** 22 | * SplClassLoader implementation that implements the technical interoperability 23 | * standards for PHP 5.3 namespaces and class names. 24 | * 25 | * http://groups.google.com/group/php-standards/web/psr-0-final-proposal?pli=1 26 | * 27 | * // Example which loads classes for the Doctrine Common package in the 28 | * // Doctrine\Common namespace. 29 | * $classLoader = new SplClassLoader('Doctrine\Common', '/path/to/doctrine'); 30 | * $classLoader->register(); 31 | * 32 | * @license http://www.opensource.org/licenses/mit-license.html MIT License 33 | * @author Jonathan H. Wage 34 | * @author Roman S. Borschel 35 | * @author Matthew Weier O'Phinney 36 | * @author Kris Wallsmith 37 | * @author Fabien Potencier 38 | */ 39 | class SplClassLoader 40 | { 41 | private $_fileExtension = '.php'; 42 | private $_namespace; 43 | private $_includePath; 44 | private $_namespaceSeparator = '\\'; 45 | 46 | /** 47 | * Creates a new SplClassLoader that loads classes of the 48 | * specified namespace. 49 | * 50 | * @param string $ns The namespace to use. 51 | */ 52 | public function __construct($ns = null, $includePath = null) 53 | { 54 | $this->_namespace = $ns; 55 | $this->_includePath = $includePath; 56 | } 57 | 58 | /** 59 | * Sets the namespace separator used by classes in the namespace of this class loader. 60 | * 61 | * @param string $sep The separator to use. 62 | */ 63 | public function setNamespaceSeparator($sep) 64 | { 65 | $this->_namespaceSeparator = $sep; 66 | } 67 | 68 | /** 69 | * Gets the namespace seperator used by classes in the namespace of this class loader. 70 | * 71 | * @return void 72 | */ 73 | public function getNamespaceSeparator() 74 | { 75 | return $this->_namespaceSeparator; 76 | } 77 | 78 | /** 79 | * Sets the base include path for all class files in the namespace of this class loader. 80 | * 81 | * @param string $includePath 82 | */ 83 | public function setIncludePath($includePath) 84 | { 85 | $this->_includePath = $includePath; 86 | } 87 | 88 | /** 89 | * Gets the base include path for all class files in the namespace of this class loader. 90 | * 91 | * @return string $includePath 92 | */ 93 | public function getIncludePath() 94 | { 95 | return $this->_includePath; 96 | } 97 | 98 | /** 99 | * Sets the file extension of class files in the namespace of this class loader. 100 | * 101 | * @param string $fileExtension 102 | */ 103 | public function setFileExtension($fileExtension) 104 | { 105 | $this->_fileExtension = $fileExtension; 106 | } 107 | 108 | /** 109 | * Gets the file extension of class files in the namespace of this class loader. 110 | * 111 | * @return string $fileExtension 112 | */ 113 | public function getFileExtension() 114 | { 115 | return $this->_fileExtension; 116 | } 117 | 118 | /** 119 | * Installs this class loader on the SPL autoload stack. 120 | */ 121 | public function register() 122 | { 123 | spl_autoload_register(array($this, 'loadClass')); 124 | } 125 | 126 | /** 127 | * Uninstalls this class loader from the SPL autoloader stack. 128 | */ 129 | public function unregister() 130 | { 131 | spl_autoload_unregister(array($this, 'loadClass')); 132 | } 133 | 134 | /** 135 | * Loads the given class or interface. 136 | * 137 | * @param string $className The name of the class to load. 138 | * @return void 139 | */ 140 | public function loadClass($className) 141 | { 142 | if (null === $this->_namespace || $this->_namespace . $this->_namespaceSeparator === substr($className, 0, strlen($this->_namespace . $this->_namespaceSeparator))) { 143 | $fileName = ''; 144 | $namespace = ''; 145 | if (false !== ($lastNsPos = strripos($className, $this->_namespaceSeparator))) { 146 | $namespace = substr($className, 0, $lastNsPos); 147 | $className = substr($className, $lastNsPos + 1); 148 | $fileName = str_replace($this->_namespaceSeparator, DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR; 149 | } 150 | $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . $this->_fileExtension; 151 | 152 | require ($this->_includePath !== null ? $this->_includePath . DIRECTORY_SEPARATOR : '') . $fileName; 153 | } 154 | } 155 | } -------------------------------------------------------------------------------- /lib/autoload.php: -------------------------------------------------------------------------------- 1 | register(); 7 | -------------------------------------------------------------------------------- /lib/utils.php: -------------------------------------------------------------------------------- 1 | '; 9 | echo 'debug from: ' . $trace[0]['file'] . ' on line:' . $trace[0]['line'] . '
'; 10 | foreach (func_get_args() as $v) { 11 | var_dump($v); 12 | } 13 | echo ''; 14 | } 15 | -------------------------------------------------------------------------------- /robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: / 3 | -------------------------------------------------------------------------------- /sample-merged-hook-request.json: -------------------------------------------------------------------------------- 1 | { 2 | "pullrequest_merged": { 3 | "description": "", 4 | "title": "Inbox changes", 5 | "close_source_branch": true, 6 | "destination": { 7 | "commit": { 8 | "hash": "82d48819e5f7", 9 | "links": { 10 | "self": { 11 | "href": "https://api.bitbucket.org/2.0/repositories/evzijst/bitbucket2/commit/82d48819e5f7" 12 | } 13 | } 14 | }, 15 | "repository": { 16 | "links": { 17 | "self": { 18 | "href": "https://api.bitbucket.org/2.0/repositories/evzijst/bitbucket2" 19 | }, 20 | "avatar": { 21 | "href": "https://bitbucket.org/m/d864f6bcaa94/img/language-avatars/default_16.png" 22 | } 23 | }, 24 | "full_name": "evzijst/bitbucket2", 25 | "name": "bitbucket2" 26 | }, 27 | "branch": { 28 | "name": "staging" 29 | } 30 | }, 31 | "reason": "", 32 | "source": { 33 | "commit": { 34 | "hash": "325625d47b0a", 35 | "links": { 36 | "self": { 37 | "href": "https://api.bitbucket.org/2.0/repositories/evzijst/bitbucket2/commit/325625d47b0a" 38 | } 39 | } 40 | }, 41 | "repository": { 42 | "links": { 43 | "self": { 44 | "href": "https://api.bitbucket.org/2.0/repositories/evzijst/bitbucket2" 45 | }, 46 | "avatar": { 47 | "href": "https://bitbucket.org/m/d864f6bcaa94/img/language-avatars/default_16.png" 48 | } 49 | }, 50 | "full_name": "evzijst/bitbucket2", 51 | "name": "bitbucket2" 52 | }, 53 | "branch": { 54 | "name": "mfrauenholtz/inbox" 55 | } 56 | }, 57 | "state": "MERGED", 58 | "author": { 59 | "username": "evzijst", 60 | "display_name": "Erik van Zijst", 61 | "links": { 62 | "self": { 63 | "href": "https://api.bitbucket.org/2.0/users/evzijst" 64 | }, 65 | "avatar": { 66 | "href": "https://bitbucket-staging-assetroot.s3.amazonaws.com/c/photos/2013/Oct/28/evzijst-avatar-3454044670-3_avatar.png" 67 | } 68 | } 69 | }, 70 | "date": "2013-11-08T19:49:12.233187+00:00" 71 | } 72 | } -------------------------------------------------------------------------------- /tmp/cache/empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ymm1x/BitbucketEventNotification/181a29ccffdbb4fbb4b836fc431d543670416c14/tmp/cache/empty -------------------------------------------------------------------------------- /tmp/empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ymm1x/BitbucketEventNotification/181a29ccffdbb4fbb4b836fc431d543670416c14/tmp/empty -------------------------------------------------------------------------------- /tmp/logs/empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ymm1x/BitbucketEventNotification/181a29ccffdbb4fbb4b836fc431d543670416c14/tmp/logs/empty -------------------------------------------------------------------------------- /vendor/empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ymm1x/BitbucketEventNotification/181a29ccffdbb4fbb4b836fc431d543670416c14/vendor/empty --------------------------------------------------------------------------------