Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

first commit

  • Loading branch information...
commit 7a7a3f0f69fd056d762a66618ea74f73f4686cf8 0 parents
Peter Jaap Blaakmeer authored
14 README.md
@@ -0,0 +1,14 @@
+Magento Exceptions to Codebase
+=================
+
+## Introduction ##
+This Magento extension inserts all the Magento exceptions to your Codebase account (see http://blog.atechmedia.com/2012/08/exception-tracking-in-codebase/).
+
+## Requirements ##
+* Codebase account (www.codebasehq.com)
+* Magento >= 1.5
+
+## Installation ##
+``cd /path/to/your/magento/root
+git clone git@github.com:elgentos/CodebaseExceptions.git CodebaseExceptions
+cp -R CodebaseExceptions/* .``
49 app/code/local/Elgentos/CodebaseExceptions/Helper/Data.php
@@ -0,0 +1,49 @@
+<?php
+
+class Elgentos_CodebaseExceptions_Helper_Data extends Mage_Core_Helper_Abstract {
+
+ public function __construct() {
+ if(Mage::getStoreConfig('codebaseexceptions/general/disabled')) return;
+
+ require_once Mage::getBaseDir('lib') . '/Airbrake/Client.php';
+ require_once Mage::getBaseDir('lib') . '/Airbrake/Configuration.php';
+
+ $apiKey = '0e806c08-1ce1-2a5f-672a-92509a8a9f81'; // This is required
+ $options = array();
+ $requestUri = explode("/",$_SERVER['REQUEST_URI']);
+ $options['action'] = array_pop($requestUri);
+ $options['component'] = implode('/',array_slice($requestUri,-2));
+ $projectRoot = explode('/',$_SERVER['PHP_SELF']);
+ array_pop($projectRoot);
+ $options['projectRoot'] = implode('/',$projectRoot).'/';
+ if(stripos($_SERVER['HTTP_HOST'],'dev.')!==false) {
+ $options['environmentName'] = 'development';
+ $devs = array('peterjaap','janhenk','wouter');
+ foreach($devs as $dev) {
+ if(substr($_SERVER['REQUEST_URI'],1,strlen($dev))==$dev) {
+ $options['environmentName'] .= ' - ' . $dev;
+ }
+ }
+ }
+ $config = new Airbrake\Configuration($apiKey,$options);
+ $this->client = new Airbrake\Client($config);
+ }
+
+ public function insertException($reportData) {
+ if(Mage::getStoreConfig('codebaseexceptions/general/disabled')) return;
+ $backtraceLines = explode("\n",$reportData[1]);
+ foreach($backtraceLines as $backtrace) {
+ $temp = array();
+ $parts = explode(': ',$backtrace);
+ $temp['function'] = $parts[1];
+ $temp['file'] = substr($parts[0],0,stripos($parts[0],'('));
+ $temp['line'] = substr($parts[0],stripos($parts[0],'(')+1,(stripos($parts[0],')')-1)-stripos($parts[0],'('));
+
+ if(!empty($temp['function'])) {
+ $backtraces[] = $temp;
+ }
+ }
+
+ $this->client->notifyOnError($reportData[0],$backtraces);
+ }
+}
8 app/code/local/Elgentos/CodebaseExceptions/controllers/IndexController.php
@@ -0,0 +1,8 @@
+<?php
+class Elgentos_CodebaseExceptions_IndexController extends Mage_Core_Controller_Front_Action {
+
+ public function testAction() {
+ throw new Exception('This is just a test exception to check whether the extension is working.');
+ }
+
+}
22 app/code/local/Elgentos/CodebaseExceptions/etc/adminhtml.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<config>
+ <acl>
+ <resources>
+ <admin>
+ <children>
+ <system>
+ <children>
+ <config>
+ <children>
+ <codebaseexceptions translate="title" module="codebaseexceptions">
+ <title>CodebaseExceptions</title>
+ </codebaseexceptions>
+ </children>
+ </config>
+ </children>
+ </system>
+ </children>
+ </admin>
+ </resources>
+ </acl>
+</config>
59 app/code/local/Elgentos/CodebaseExceptions/etc/config.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0"?>
+<config>
+ <modules>
+ <Elgentos_CodebaseExceptions>
+ <version>0.1.0</version>
+ </Elgentos_CodebaseExceptions>
+ </modules>
+ <adminhtml>
+ <translate>
+ <modules>
+ <Elgentos_CodebaseExceptions>
+ <files>
+ <default>Elgentos_CodebaseExceptions.csv</default>
+ </files>
+ </Elgentos_CodebaseExceptions>
+ </modules>
+ </translate>
+
+ <acl>
+ <resources>
+ <admin>
+ <children>
+ <system>
+ <children>
+ <config>
+ <children>
+ <codebaseexceptions translate="title" module="codebaseexceptions">
+ <title>CodebaseExceptions</title>
+ </codebaseexceptions>
+ </children>
+ </config>
+ </children>
+ </system>
+ </children>
+ </admin>
+ </resources>
+ </acl>
+ </adminhtml>
+
+ <frontend>
+ <routers>
+ <exceptions>
+ <use>standard</use>
+ <args>
+ <module>Elgentos_CodebaseExceptions</module>
+ <frontName>exceptions</frontName>
+ </args>
+ </exceptions>
+ </routers>
+ </frontend>
+
+ <global>
+ <helpers>
+ <codebaseexceptions>
+ <class>Elgentos_CodebaseExceptions_Helper</class>
+ </codebaseexceptions>
+ </helpers>
+ </global>
+</config>
50 app/code/local/Elgentos/CodebaseExceptions/etc/system.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+<config>
+ <tabs>
+ <elgentos>
+ <label>Elgentos</label>
+ <sort_order>195</sort_order>
+ </elgentos>
+ </tabs>
+ <sections>
+ <codebaseexceptions translate="label" module="codebaseexceptions">
+ <label>CodebaseExceptions</label>
+ <tab>elgentos</tab>
+ <frontend_type>text</frontend_type>
+ <sort_order>71</sort_order>
+ <show_in_default>1</show_in_default>
+ <show_in_website>1</show_in_website>
+ <show_in_store>1</show_in_store>
+ <groups>
+ <general translate="label">
+ <label>General</label>
+ <expanded>1</expanded>
+ <sort_order>600</sort_order>
+ <show_in_default>1</show_in_default>
+ <show_in_website>1</show_in_website>
+ <show_in_store>1</show_in_store>
+ <fields>
+ <disabled translate="label">
+ <label>Disable module</label>
+ <frontend_type>select</frontend_type>
+ <source_model>adminhtml/system_config_source_yesno</source_model>
+ <sort_order>10</sort_order>
+ <show_in_default>1</show_in_default>
+ <show_in_website>1</show_in_website>
+ <show_in_store>1</show_in_store>
+ </disabled>
+ <apikey translate="label">
+ <label>Codebase Exceptions API key</label>
+ <frontend_type>text</frontend_type>
+
+ <sort_order>20</sort_order>
+ <show_in_default>1</show_in_default>
+ <show_in_website>1</show_in_website>
+ <show_in_store>1</show_in_store>
+ </apikey>
+ </fields>
+ </general>
+ </groups>
+ </codebaseexceptions>
+ </sections>
+</config>
9 app/etc/modules/Elgentos_CodebaseExceptions.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+ <config>
+ <modules>
+ <Elgentos_CodebaseExceptions>
+ <active>true</active>
+ <codePool>local</codePool>
+ </Elgentos_CodebaseExceptions>
+ </modules>
+ </config>
36 errors/report.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Magento
+ *
+ * NOTICE OF LICENSE
+ *
+ * This source file is subject to the Open Software License (OSL 3.0)
+ * that is bundled with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://opensource.org/licenses/osl-3.0.php
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@magentocommerce.com so we can send you a copy immediately.
+ *
+ * DISCLAIMER
+ *
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
+ * versions in the future. If you wish to customize Magento for your
+ * needs please refer to http://www.magentocommerce.com for more information.
+ *
+ * @category Mage
+ * @package Errors
+ * @copyright Copyright (c) 2012 Magento Inc. (http://www.magentocommerce.com)
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
+ */
+
+require_once 'processor.php';
+
+$processor = new Error_Processor();
+
+if (isset($reportData) && is_array($reportData)) {
+ $processor->saveReport($reportData);
+ Mage::helper('codebaseexceptions')->insertException($reportData);
+}
+
+$processor->processReport();
120 lib/Airbrake/Client.php
@@ -0,0 +1,120 @@
+<?php
+namespace Airbrake;
+
+use Exception;
+
+require_once realpath(__DIR__.'/Record.php');
+require_once realpath(__DIR__.'/Configuration.php');
+require_once realpath(__DIR__.'/Connection.php');
+require_once realpath(__DIR__.'/Version.php');
+require_once realpath(__DIR__.'/Exception.php');
+require_once realpath(__DIR__.'/Notice.php');
+require_once realpath(__DIR__.'/Resque/NotifyJob.php');
+
+/**
+ * Airbrake client class.
+ *
+ * @package Airbrake
+ * @author Drew Butler <drew@abstracting.me>
+ * @copyright (c) 2011 Drew Butler
+ * @license http://www.opensource.org/licenses/mit-license.php
+ */
+class Client
+{
+ protected $configuration = null;
+ protected $connection = null;
+ protected $notice = null;
+
+ /**
+ * Build the Client with the Airbrake Configuration.
+ *
+ * @throws Airbrake\Exception
+ * @param Configuration $configuration
+ */
+ public function __construct(Configuration $configuration)
+ {
+ $configuration->verify();
+
+ $this->configuration = $configuration;
+ $this->connection = new Connection($configuration);
+ }
+
+ /**
+ * Notify on an error message.
+ *
+ * @param string $message
+ * @param array $backtrace
+ * @return string
+ */
+ public function notifyOnError($message, array $backtrace = null)
+ {
+ if (!$backtrace) {
+ $backtrace = debug_backtrace();
+ if (count($backtrace) > 1) {
+ array_shift($backtrace);
+ }
+ }
+
+ $notice = new Notice;
+ $notice->load(array(
+ 'errorClass' => 'PHP Error',
+ 'backtrace' => $backtrace,
+ 'errorMessage' => $message,
+ ));
+
+ return $this->notify($notice);
+ }
+
+ /**
+ * Notify on an exception
+ *
+ * @param Airbrake\Notice $notice
+ * @return string
+ */
+ public function notifyOnException(Exception $exception)
+ {
+ $notice = new Notice;
+ $notice->load(array(
+ 'errorClass' => get_class($exception),
+ 'backtrace' => $this->cleanBacktrace($exception->getTrace() ?: debug_backtrace()),
+ 'errorMessage' => $exception->getMessage(),
+ ));
+
+ return $this->notify($notice);
+ }
+
+ /**
+ * Notify about the notice.
+ *
+ * If there is a PHP Resque client given in the configuration, then use that to queue up a job to
+ * send this out later. This should help speed up operations.
+ *
+ * @param Airbrake\Notice $notice
+ */
+ public function notify(Notice $notice)
+ {
+ if (class_exists('Resque') && $this->configuration->queue) {
+ //print_r($notice);exit;
+ $data = array('notice' => serialize($notice), 'configuration' => serialize($this->configuration));
+ \Resque::enqueue($this->configuration->queue, 'Airbrake\\Resque\\NotifyJob', $data);
+ return;
+ }
+
+ return $this->connection->send($notice);
+ }
+
+ /**
+ * Clean the backtrace of unneeded junk.
+ *
+ * @param array $backtrace
+ * @return array
+ */
+ protected function cleanBacktrace($backtrace)
+ {
+ foreach ($backtrace as &$item) {
+ unset($item['args']);
+ }
+
+ return $backtrace;
+ }
+}
98 lib/Airbrake/Configuration.php
@@ -0,0 +1,98 @@
+<?php
+namespace Airbrake;
+
+require_once 'Record.php';
+
+use Airbrake\Exception as AirbrakeException;
+
+/**
+ * Airbrake configuration class.
+ *
+ * Loads via the inherited Record class methods.
+ *
+ * @package Airbrake
+ * @author Drew Butler <drew@abstracting.me>
+ * @copyright (c) 2011 Drew Butler
+ * @license http://www.opensource.org/licenses/mit-license.php
+ */
+class Configuration extends Record
+{
+ protected $_apiKey;
+ protected $_timeout = 2;
+ protected $_environmentName = 'production';
+ protected $_serverData;
+ protected $_getData;
+ protected $_postData;
+ protected $_sessionData;
+ protected $_component;
+ protected $_action;
+ protected $_projectRoot;
+ protected $_url;
+ protected $_hostname;
+ protected $_queue;
+ protected $_apiEndPoint = 'http://exceptions.codebasehq.com/notifier_api/v2/notices';
+
+ /**
+ * Load the given data array to the record.
+ *
+ * @param string $apiKey
+ * @param array|stdClass $data
+ */
+ public function __construct($apiKey, $data = array())
+ {
+ $data['apiKey'] = $apiKey;
+ parent::__construct($data);
+ }
+
+ /**
+ * Initialize the data source.
+ */
+ protected function initialize()
+ {
+ if (!$this->serverData) {
+ $this->serverData = (array) $_SERVER;
+ }
+ if (!$this->getData) {
+ $this->getData = (array) $_GET;
+ }
+ if (!$this->postData) {
+ $this->postData = (array) $_POST;
+ }
+
+ if (!$this->sessionData && isset($_SESSION)) {
+ $this->sessionData = (array) $_SESSION;
+ }
+
+ if (!$this->projectRoot) {
+ $this->projectRoot = isset($this->serverData['_']) ? $this->serverData['_'] : $this->serverData['DOCUMENT_ROOT'];
+ }
+
+ if (!$this->url) {
+ $this->url = isset($this->serverData['REDIRECT_URL']) ? $this->serverData['REDIRECT_URL'] : $this->serverData['SCRIPT_NAME'];
+ }
+
+ if (!$this->hostname) {
+ $this->hostname = isset($this->serverData['HTTP_HOST']) ? $this->serverData['HTTP_HOST'] : 'No Host';
+ }
+ }
+
+ /**
+ * Get the combined server parameters.
+ *
+ * @return array
+ */
+ public function getParamters()
+ {
+ return array_merge($this->get('postData'), $this->get('getData'));
+ }
+
+ /**
+ * Verify that the configuration is complete.
+ */
+ public function verify()
+ {
+ if (!$this->apiKey) {
+ throw new AirbrakeException('Cannot initialize the Airbrake client without an ApiKey being set to the configuration.');
+ }
+ }
+}
66 lib/Airbrake/Connection.php
@@ -0,0 +1,66 @@
+<?php
+namespace Airbrake;
+
+/**
+ * Airbrake connection class.
+ *
+ * @package Airbrake
+ * @author Drew Butler <drew@abstracting.me>
+ * @copyright (c) 2011 Drew Butler
+ * @license http://www.opensource.org/licenses/mit-license.php
+ */
+class Connection
+{
+
+ protected $configuration = null;
+ protected $headers = array();
+
+ /**
+ * Build the object with the airbrake Configuration.
+ *
+ * @param Airbrake\Configuration $configuration
+ */
+ public function __construct(Configuration $configuration)
+ {
+ $this->configuration = $configuration;
+
+ $this->addHeader(array(
+ 'Accept: text/xml, application/xml',
+ 'Content-Type: text/xml'
+ ));
+ }
+
+ /**
+ * Add a header to the connection.
+ *
+ * @param string header
+ */
+ public function addHeader($header)
+ {
+ $this->headers += (array)$header;
+ }
+
+ /**
+ * @param Airbrake\Notice $notice
+ * @return string
+ **/
+ public function send(Notice $notice)
+ {
+ $curl = curl_init();
+
+ $xml = $notice->toXml($this->configuration);
+
+ curl_setopt($curl, CURLOPT_URL, $this->configuration->apiEndPoint);
+ curl_setopt($curl, CURLOPT_POST, 1);
+ curl_setopt($curl, CURLOPT_HEADER, 0);
+ curl_setopt($curl, CURLOPT_TIMEOUT, $this->configuration->timeout);
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $xml);
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $this->headers);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
+
+ $return = curl_exec($curl);
+ curl_close($curl);
+
+ return $return;
+ }
+}
172 lib/Airbrake/EventHandler.php
@@ -0,0 +1,172 @@
+<?php
+namespace Airbrake;
+
+use Exception;
+
+require_once 'Client.php';
+require_once 'Configuration.php';
+
+/**
+ * Airbrake EventHandler class.
+ *
+ * @package Airbrake
+ * @author Drew Butler <drew@abstracting.me>
+ * @copyright (c) 2011 Drew Butler
+ * @license http://www.opensource.org/licenses/mit-license.php
+ */
+class EventHandler
+{
+ /**
+ * The singleton instance
+ */
+ protected static $instance = null;
+ protected $airbrakeClient = null;
+ protected $notifyOnWarning = null;
+
+ protected $warningErrors = array ( \E_NOTICE => 'Notice',
+ \E_STRICT => 'Strict',
+ \E_USER_WARNING => 'User Warning',
+ \E_USER_NOTICE => 'User Notice',
+ \E_DEPRECATED => 'Deprecated',
+ \E_USER_DEPRECATED => 'User Deprecated',
+ \E_CORE_WARNING => 'Core Warning' );
+
+ protected $fatalErrors = array ( \E_ERROR => 'Error',
+ \E_PARSE => 'Parse',
+ \E_COMPILE_WARNING => 'Compile Warning',
+ \E_COMPILE_ERROR => 'Compile Error',
+ \E_CORE_ERROR => 'Core Error',
+ \E_WARNING => 'Warning',
+ \E_USER_ERROR => 'User Error',
+ \E_RECOVERABLE_ERROR => 'Recoverable Error' );
+
+ /**
+ * Build with the Airbrake client class.
+ *
+ * @param Airbrake\Client $client
+ */
+ public function __construct(Client $client, $notifyOnWarning)
+ {
+ $this->notifyOnWarning = $notifyOnWarning;
+ $this->airbrakeClient = $client;
+ }
+
+ /**
+ * Get the current handler.
+ *
+ * @param string $apiKey
+ * @param bool $notifyOnWarning
+ * @param array $options
+ * @return EventHandler
+ */
+ public static function start($apiKey, $notifyOnWarning=false, array $options=array())
+ {
+ if ( !isset(self::$instance)) {
+ $config = new Configuration($apiKey, $options);
+
+ $client = new Client($config);
+ self::$instance = new self($client, $notifyOnWarning);
+
+ set_error_handler(array(self::$instance, 'onError'));
+ set_exception_handler(array(self::$instance, 'onException'));
+ register_shutdown_function(array(self::$instance, 'onShutdown'));
+ }
+
+ return self::$instance;
+ }
+
+
+ /**
+ * Revert the handlers back to their original state.
+ */
+ public static function reset()
+ {
+ if (isset(self::$instance)) {
+ restore_error_handler();
+ restore_exception_handler();
+ }
+
+ self::$instance = null;
+ }
+
+ /**
+ * Catches standard PHP style errors
+ *
+ * @see http://us3.php.net/manual/en/function.set-error-handler.php
+ * @param int $type
+ * @param string $message
+ * @param string $file
+ * @param string $line
+ * @param array $context
+ * @return bool
+ */
+ public function onError($type, $message, $file = null, $line = null, $context = null)
+ {
+ // This will catch silenced @ function calls and keep them quiet.
+ if (ini_get('error_reporting') == 0) {
+ return true;
+ }
+
+ $backtrace = debug_backtrace();
+ array_shift( $backtrace );
+
+ if (isset($this->fatalErrors[$type])) {
+ throw new Exception(sprintf('A PHP error occurred (%s). %s', $this->fatalErrors[$type], $message));
+ }
+
+ if ($this->notifyOnWarning && isset ( $this->warningErrors[$type])) {
+ $message = sprintf('A PHP warning occurred (%s). %s', $this->warningErrors[$type], $message);
+ $this->airbrakeClient->notifyOnError($message);
+ return true;
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Catches uncaught exceptions.
+ *
+ * @see http://us3.php.net/manual/en/function.set-exception-handler.php
+ * @param Exception $exception
+ * @return bool
+ */
+ public function onException(Exception $exception)
+ {
+ $this->airbrakeClient->notifyOnException($exception);
+
+ return true;
+ }
+
+ /**
+ * Handles the PHP shutdown event.
+ *
+ * This event exists almost soley to provide a means to catch and log errors that might have been
+ * otherwise lost when PHP decided to die unexpectedly.
+ */
+ public function onShutdown()
+ {
+ // If the instance was unset, then we shouldn't run.
+ if (self::$instance == null) {
+ return;
+ }
+
+ // This will help prevent multiple calls to this, incase the shutdown handler was declared
+ // multiple times. This only should occur in unit tests, when the handlers are created
+ // and removed repeatedly. As we cannot remove shutdown handlers, this prevents us from
+ // calling it 1000 times at the end.
+ self::$instance = null;
+
+ // Get the last error if there was one, if not, let's get out of here.
+ if (!$error = error_get_last()) {
+ return;
+ }
+
+ $message = 'It looks like we may have shutdown unexpectedly. Here is the error '
+ . 'we saw while closing up: %s File: %s Line: %i';
+
+ $message = sprintf($message, $error['message'], $error['file'], $error['line']);
+
+ $this->airbrakeClient->notifyOnError($message);
+ }
+}
15 lib/Airbrake/Exception.php
@@ -0,0 +1,15 @@
+<?php
+namespace Airbrake;
+
+/**
+ * Airbrake exception.
+ *
+ * @package Airbrake
+ * @author Drew Butler <drew@abstracting.me>
+ * @copyright (c) 2011 Drew Butler
+ * @license http://www.opensource.org/licenses/mit-license.php
+ */
+class Exception extends \Exception
+{
+
+}
102 lib/Airbrake/Notice.php
@@ -0,0 +1,102 @@
+<?php
+namespace Airbrake;
+
+use SimpleXMLElement;
+
+/**
+ * Airbrake notice class.
+ *
+ * @package Airbrake
+ * @author Drew Butler <drew@abstracting.me>
+ * @copyright (c) 2011 Drew Butler
+ * @license http://www.opensource.org/licenses/mit-license.php
+ */
+class Notice extends Record
+{
+ /**
+ * The backtrace from the given exception or hash.
+ */
+ protected $_backtrace = null;
+
+ /**
+ * The name of the class of error (such as RuntimeError)
+ */
+ protected $_errorClass = null;
+
+ /**
+ * The message from the exception, or a general description of the error
+ */
+ protected $_errorMessage = null;
+
+ /**
+ * Convert the notice to xml
+ *
+ * @param Airbrake\Configuration $configuration
+ * @return string
+ */
+ public function toXml(Configuration $configuration)
+ {
+ $doc = new SimpleXMLElement('<notice />');
+ $doc->addAttribute('version', Version::API);
+ $doc->addChild('api-key', $configuration->get('apiKey'));
+
+ $notifier = $doc->addChild('notifier');
+ $notifier->addChild('name', Version::NAME);
+ $notifier->addChild('version', Version::NUMBER);
+ $notifier->addChild('url', Version::APP_URL);
+
+ $env = $doc->addChild('server-environment');
+ $env->addChild('project-root', $configuration->get('projectRoot'));
+ $env->addChild('environment-name', $configuration->get('environmentName'));
+
+ $error = $doc->addChild('error');
+ $error->addChild('class', $this->errorClass);
+ $error->addChild('message', $this->errorMessage);
+
+ if (count($this->backtrace) > 0) {
+ $backtrace = $error->addChild('backtrace');
+ foreach ($this->backtrace as $entry) {
+ $line = $backtrace->addChild('line');
+ $line->addAttribute('file', isset($entry['file']) ? $entry['file'] : '');
+ $line->addAttribute('number', isset($entry['line']) ? $entry['line'] : '');
+ $line->addAttribute('method', isset($entry['function']) ? $entry['function'] : '');
+ }
+ }
+
+ $request = $doc->addChild('request');
+ $request->addChild('url', $configuration->get('url'));
+ $request->addChild('component', $configuration->get('component'));
+ $request->addChild('action', $configuration->get('action'));
+
+ $this->array2Node($request, 'params', $configuration->getParamters());
+ $this->array2Node($request, 'session', $configuration->get('sessionData'));
+ $this->array2Node($request, 'cgi-data', $configuration->get('serverData'));
+
+ return $doc->asXML();
+ }
+
+ /**
+ * Add a Airbrake var block to an XML node.
+ *
+ * @param SimpleXMLElement $parentNode
+ * @param string $key
+ * @param array $params
+ **/
+ protected function array2Node($parentNode, $key, $params)
+ {
+ if (count($params) == 0) {
+ return;
+ }
+
+ $node = $parentNode->addChild($key);
+ foreach ($params as $key => $value) {
+ if (is_array($value) || is_object($value)) {
+ $value = json_encode((array) $value);
+ }
+
+ // htmlspecialchars() is needed to prevent html characters from breaking the node.
+ $node->addChild('var', htmlspecialchars($value))
+ ->addAttribute('key', $key);
+ }
+ }
+}
241 lib/Airbrake/Record.php
@@ -0,0 +1,241 @@
+<?php
+namespace Airbrake;
+
+use ArrayAccess, IteratorAggregate, ArrayIterator;
+
+/**
+ * A record abstract that can help accelerate building models.
+ *
+ * The extended object simply needs to define what the properties of the object
+ * are going to be. An example of this is as follows:
+ *
+ * <pre>
+ * class Person extends Record
+ * {
+ * protected $_FirstName;
+ * protected $_LastName;
+ * protected $_Age;
+ * }
+ * </pre>
+ *
+ * Now you can simply retrieve these properties by requesting them by their key name
+ * minus the prefixed '_'. So, if you were to call ->get( 'FirstName' ) it would
+ * retrieve that key for you. Similarly, you can call set( 'FirstName', 'Drew' ) and
+ * it will set that key. Give load() an array or stdClass of key value pairs and it
+ * will parse those into their matching keys. Any key that is given that does not
+ * exist in the parameters will be ignored.
+ *
+ * These objects may also be accessed and iterated over as if they were arrays. This means
+ * that if you prefer the $obj['key'] syntax, you are free to use it. Any keys that are set
+ * to it that are not known to the record type will be ignored and any that do not exist
+ * when getting, will simply return null.
+ *
+ * @package Airbrake
+ * @author Drew Butler <drew@abstracting.me>
+ * @copyright (c) 2011 Drew Butler
+ * @license http://www.opensource.org/licenses/mit-license.php
+ */
+abstract class Record implements ArrayAccess, IteratorAggregate
+{
+ const PREFIX = '_';
+
+
+ /**
+ * Load the given data array to the record.
+ *
+ * @param array|stdClass $data
+ */
+ public function __construct($data = array())
+ {
+ $this->load($data);
+ $this->initialize();
+ }
+
+ /**
+ * Get the value for the given key.
+ *
+ * The given key should match one of the parameters about, but with out the
+ * prefix. That is added on during this process.
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function get($key)
+ {
+ if ($this->exists($key)) {
+ $key = self::PREFIX.$key;
+ return $this->$key;
+ }
+
+ return null;
+ }
+
+ /**
+ * Magic alias for the get() method.
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function __get($key)
+ {
+ return $this->get($key);
+ }
+
+ /**
+ * Set the given value to the given key.
+ *
+ * The given key should match one of the parameters about, but with out the
+ * prefix. That is added on during this process.
+ *
+ * @param string $key
+ * @param mixed $value
+ */
+ public function set($key, $value)
+ {
+ if ($this->exists($key)) {
+ $key = self::PREFIX.$key;
+ $this->$key = $value;
+ }
+ }
+
+ /**
+ * Magic alias for the set() method.
+ *
+ * @param string $key
+ * @param mixed $value
+ */
+ public function __set($key, $value)
+ {
+ return $this->set($key, $value);
+ }
+
+ /**
+ * Load the given data array to the record.
+ *
+ * @param array|stdClass $data
+ */
+ public function load($data)
+ {
+ if (!is_array($data) && !$data instanceof \stdClass) {
+ return;
+ }
+
+ foreach ($data as $key => $value) {
+ $this->set($key, $value);
+ }
+ }
+
+ /**
+ * Dump the data into an array.
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ $data = array();
+ $vars = get_object_vars($this);
+
+ foreach ($vars as $key => $value) {
+ if ($key[0] === self::PREFIX) {
+ $key = substr($key, 1, strlen($key) - 1);
+
+ if ($value instanceof Record) {
+ $value = $value->toArray();
+ }
+
+ $data[$key] = $value;
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * Is the given key set in this record?
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function exists($key)
+ {
+ return property_exists($this, self::PREFIX.$key);
+ }
+
+ /**
+ * Get the keys that are contained in this record.
+ *
+ * @return array
+ */
+ public function getKeys()
+ {
+ return array_keys($this->toArray());
+ }
+
+ /**
+ * Set the given value for the given key
+ *
+ * Part of the ArrayAccess interface.
+ *
+ * @param string $key
+ * @param mixed $value
+ */
+ public function offsetSet($key, $value)
+ {
+ if (!is_null($key)) {
+ $this->set($key, $value);
+ }
+ }
+
+ /**
+ * Is the given key available?
+ *
+ * Part of the ArrayAccess interface.
+ *
+ * @return string $key
+ * @return bool
+ */
+ public function offsetExists($key)
+ {
+ return $this->exists($key);
+ }
+
+ /**
+ * Set the given key to null
+ *
+ * Part of the ArrayAccess interface.
+ *
+ * @param string $key
+ */
+ public function offsetUnset($key)
+ {
+ $this->set($key, null);
+ }
+
+ /**
+ * Get the value for the given key.
+ *
+ * Part of the ArrayAccess interface.
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function offsetGet($key)
+ {
+ return $this->get($key);
+ }
+
+ /**
+ * Get the iterator for the IteratorAggregate interface.
+ *
+ * @return ArrayIterator
+ */
+ public function getIterator()
+ {
+ return new ArrayIterator($this->dump());
+ }
+
+ /**
+ * Optional method to declare that will initialize the data on construct.
+ */
+ protected function initialize() {}
+}
18 lib/Airbrake/Resque/NotifyJob.php
@@ -0,0 +1,18 @@
+<?php
+namespace Airbrake\Resque;
+
+require_once realpath(__DIR__.'/../Client.php');
+
+use Airbrake\Connection;
+
+class NotifyJob
+{
+ public function perform()
+ {
+ $notice = unserialize($this->args['notice']);
+ $configuration = unserialize($this->args['configuration']);
+
+ $connection = new Connection($configuration);
+ echo $connection->send($notice);
+ }
+}
18 lib/Airbrake/Version.php
@@ -0,0 +1,18 @@
+<?php
+namespace Airbrake;
+
+/**
+ * Airbrake notice class.
+ *
+ * @package Airbrake
+ * @author Drew Butler <drew@abstracting.me>
+ * @copyright (c) 2011 Drew Butler
+ * @license http://www.opensource.org/licenses/mit-license.php
+ */
+class Version
+{
+ const NAME = 'nodrew-php-airbrake';
+ const NUMBER = '1.0';
+ const APP_URL = 'https://github.com/nodrew/php-airbrake';
+ const API = '2.0';
+}
Please sign in to comment.
Something went wrong with that request. Please try again.