Skip to content

Commit

Permalink
Remove the dependency on Horde's YAML library.
Browse files Browse the repository at this point in the history
Add ability to specify the environment the app is running in.
Support v2 of the Hoptoad API.
  • Loading branch information
rich committed Jan 23, 2010
1 parent 9448129 commit 26a1e7a
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 130 deletions.
292 changes: 173 additions & 119 deletions Hoptoad.php
@@ -1,123 +1,177 @@
<?php <?php
if (!class_exists('HTTP_Request')) require_once('HTTP/Request.php'); if (!class_exists('HTTP_Request2')) require_once('HTTP/Request2.php');
if (!class_exists('Horde_Yaml')) require_once('Horde/Yaml.php'); if (!class_exists('HTTP_Request2_Adapter_Socket')) require_once 'HTTP/Request2/Adapter/Socket.php';
if (!class_exists('Horde_Yaml_Dumper')) require_once('Horde/Yaml/Dumper.php');


class Hoptoad class Hoptoad
{ {
/** const NOTIFIER_NAME = 'php-hoptoad-notifier';
* Install the error and exception handlers that connect to Hoptoad const NOTIFIER_VERSION = '0.2.0';
* const NOTIFIER_URL = 'http://github.com/rich/php-hoptoad-notifier';
* @return void const NOTIFIER_API_VERSION = '2.0';
* @author Rich Cavanaugh
*/ /**
public static function installHandlers($api_key=NULL) * Install the error and exception handlers that connect to Hoptoad
{ *
if (isset($api_key)) define('HOPTOAD_API_KEY', $api_key); * @return void

* @author Rich Cavanaugh
set_error_handler(array("Hoptoad", "errorHandler")); */
set_exception_handler(array("Hoptoad", "exceptionHandler")); public static function installHandlers($api_key=NULL, $environment=NULL)
} {

if (isset($api_key)) define('HOPTOAD_API_KEY', $api_key);
/** if (isset($environment)) define('HOPTOAD_APP_ENVIRONMENT', $environment);
* Handle a php error
* set_error_handler(array("Hoptoad", "errorHandler"));
* @param string $code set_exception_handler(array("Hoptoad", "exceptionHandler"));
* @param string $message }
* @param string $file
* @param string $line /**
* @return void * Handle a php error
* @author Rich Cavanaugh *
*/ * @param string $code
public static function errorHandler($code, $message, $file, $line) * @param string $message
{ * @param string $file
if ($code == E_STRICT) return; * @param string $line

* @return void
$trace = Hoptoad::tracer(); * @author Rich Cavanaugh
Hoptoad::notifyHoptoad(HOPTOAD_API_KEY, $message, $file, $line, $trace, null); */
} public static function errorHandler($code, $message, $file, $line)

{
/** if ($code == E_STRICT) return;
* Handle a raised exception
* $trace = Hoptoad::tracer();
* @param string $exception Hoptoad::notifyHoptoad(HOPTOAD_API_KEY, $message, $file, $line, $trace, null, HOPTOAD_APP_ENVIRONMENT);
* @return void }
* @author Rich Cavanaugh
*/ /**
public static function exceptionHandler($exception) * Handle a raised exception
{ *
$trace = Hoptoad::tracer($exception->getTrace()); * @param string $exception

* @return void
Hoptoad::notifyHoptoad(HOPTOAD_API_KEY, $exception->getMessage(), $exception->getFile(), $exception->getLine(), $trace, null); * @author Rich Cavanaugh
} */

public static function exceptionHandler($exception)
/** {
* Pass the error and environment data on to Hoptoad $trace = Hoptoad::tracer($exception->getTrace());
*
* @package default Hoptoad::notifyHoptoad(HOPTOAD_API_KEY, $exception->getMessage(), $exception->getFile(), $exception->getLine(), $trace, null, HOPTOAD_APP_ENVIRONMENT);
* @author Rich Cavanaugh }
*/
public static function notifyHoptoad($api_key, $message, $file, $line, $trace, $error_class=null) /**
{ * Pass the error and environment data on to Hoptoad
$req =& new HTTP_Request("http://hoptoadapp.com/notices/", array("method" => "POST", "timeout" => 2)); *
$req->addHeader('Accept', 'text/xml, application/xml'); * @package default
$req->addHeader('Content-type', 'application/x-yaml'); * @author Rich Cavanaugh

*/
array_unshift($trace, "$file:$line"); public static function notifyHoptoad($api_key, $message, $file, $line, $trace, $error_class=null, $environment='production')

{
if (isset($_SESSION)) { array_unshift($trace, "$file:$line");
$session = array('key' => session_id(), 'data' => $_SESSION);
} else { $adapter = new HTTP_Request2_Adapter_Socket;
$session = array(); $req = new HTTP_Request2("http://hoptoadapp.com/notifier_api/v2/notices", HTTP_Request2::METHOD_POST);
} $req->setAdapter($adapter);

$req->setHeader(array(
$url = "http://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}"; 'Accept' => 'text/xml, application/xml',
$body = array( 'Content-Type' => 'text/xml'
'api_key' => $api_key, ));
'error_class' => $error_class, $req->setBody(self::buildXmlNotice($api_key, $message, $trace, $error_class, $environment));
'error_message' => $message, echo $req->send()->getBody();
'backtrace' => $trace, }
'request' => array("params" => $_REQUEST, "url" => $url),
'session' => $session, /**
'environment' => $_SERVER * Build up the XML to post according to the documentation at:
); * http://help.hoptoadapp.com/faqs/api-2/notifier-api-v2

* @return string
$req->setBody(Horde_Yaml::dump(array("notice" => $body))); * @author Rich Cavanaugh
$req->sendRequest(); **/
} public static function buildXmlNotice($api_key, $message, $trace, $error_class, $environment, $component='')

{
/** $doc = new SimpleXMLElement('<notice />');
* Build a trace that is formatted in the way Hoptoad expects $doc->addAttribute('version', self::NOTIFIER_API_VERSION);
* $doc->addChild('api-key', $api_key);
* @param string $trace
* @return void $notifier = $doc->addChild('notifier');
* @author Rich Cavanaugh $notifier->addChild('name', self::NOTIFIER_NAME);
*/ $notifier->addChild('version', self::NOTIFIER_VERSION);
public static function tracer($trace = NULL) $notifier->addChild('url', self::NOTIFIER_URL);
{
$lines = Array(); $error = $doc->addChild('error');

$error->addChild('class', $error_class);
$trace = $trace ? $trace : debug_backtrace(); $error->addChild('message', $message);


$indent = ''; $backtrace = $error->addChild('backtrace');
$func = ''; foreach ($trace as $line) {

$line_node = $backtrace->addChild('line');
foreach($trace as $val) { list($file, $number) = explode(':', $line);
if (isset($val['class']) && $val['class'] == 'Hoptoad') continue; $line_node->addAttribute('file', $file);

$line_node->addAttribute('number', $number);
$file = isset($val['file']) ? $val['file'] : 'Unknown file'; $line_node->addAttribute('method', '');
$line_number = isset($val['line']) ? $val['line'] : ''; }
$func = isset($val['function']) ? $val['function'] : '';
$class = isset($val['class']) ? $val['class'] : ''; $request = $doc->addChild('request');

$request->addChild('url', "http://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}");
$line = $file; $request->addChild('component', $component);
if ($line_number) $line .= ':' . $line_number;
if ($func) $line .= ' in function ' . $func; if (isset($_REQUEST) && !empty($_REQUEST)) {
if ($class) $line .= ' in class ' . $class; $params = $request->addChild('params');

foreach ($_REQUEST as $key => $val) {
$lines[] = $line; $var_node = $params->addChild('var', $val);
} $var_node->addAttribute('key', $key);

}
return $lines; }
}
} if (isset($_SESSION) && !empty($_SESSION)) {
$session = $request->addChild('session');
foreach ($_SESSION as $key => $val) {
$var_node = $session->addChild('var', $val);
$var_node->addAttribute('key', $key);
}
}

$cgi_data = $request->addChild('cgi-data');
foreach ($_SERVER as $key => $val) {
$var_node = $cgi_data->addChild('var', $val);
$var_node->addAttribute('key', $key);
}

$env = $doc->addChild('server-environment');
$env->addChild('project-root', $_SERVER['DOCUMENT_ROOT']);
$env->addChild('environment-name', $environment);

return $doc->asXML();
}

/**
* Build a trace that is formatted in the way Hoptoad expects
*
* @param string $trace
* @return void
* @author Rich Cavanaugh
*/
public static function tracer($trace = NULL)
{
$lines = Array();

$trace = $trace ? $trace : debug_backtrace();

$indent = '';
$func = '';

foreach($trace as $val) {
if (isset($val['class']) && $val['class'] == 'Hoptoad') continue;

$file = isset($val['file']) ? $val['file'] : 'Unknown file';
$line_number = isset($val['line']) ? $val['line'] : '';
$func = isset($val['function']) ? $val['function'] : '';
$class = isset($val['class']) ? $val['class'] : '';

$line = $file;
if ($line_number) $line .= ':' . $line_number;
if ($func) $line .= ' in function ' . $func;
if ($class) $line .= ' in class ' . $class;

$lines[] = $line;
}

return $lines;
}
}
15 changes: 5 additions & 10 deletions README.markdown
@@ -1,6 +1,6 @@
# Introduction # Introduction


This is a simple [Hoptoad](http://hoptoadapp.com) notifier for PHP. It's been used in a few production sites now with success. It's not quite as fully featured as the official Ruby notifier but it works well. This is a simple [Hoptoad](http://hoptoadapp.com) notifier for PHP. It's been used in a few production sites now with success. It's not quite as fully featured as the official Ruby notifier but it works well.


# Limitations # Limitations


Expand All @@ -10,18 +10,13 @@ For deploy tracking, since I use Capistrano to deploy my PHP apps, I simply use


# Requirements # Requirements


The notifier uses the Horde_Yaml class. You can install this class using the commands below. Install Pear's HTTP_Request2:


pear channel-discover pear.horde.org pear install HTTP_Request2
pear install horde/yaml

It also uses Pear's HTTP_Request:

pear install HTTP_Request


# License # License


Copyright (c) 2009, Rich Cavanaugh Copyright (c) 2010, Rich Cavanaugh
All rights reserved. All rights reserved.


Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Expand All @@ -30,4 +25,4 @@ Redistribution and use in source and binary forms, with or without modification,
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. * The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.


THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2 changes: 1 addition & 1 deletion example.php
@@ -1,4 +1,4 @@
<?php <?php
require_once('Hoptoad.php'); require_once('Hoptoad.php');


Hoptoad::installHandlers("YOUR_HOPTOAD_API_KEY"); Hoptoad::installHandlers("YOUR_HOPTOAD_API_KEY", 'production');

0 comments on commit 26a1e7a

Please sign in to comment.