From 5758426a5ec2e4ac4a87c2bcda2537e789f541e4 Mon Sep 17 00:00:00 2001 From: Chris Oake Date: Mon, 30 Jul 2012 15:12:42 -0600 Subject: [PATCH 01/14] init --- Monolog/Formatter/ChromePHPFormatter.php | 79 +++ Monolog/Formatter/FormatterInterface.php | 36 ++ Monolog/Formatter/GelfMessageFormatter.php | 94 ++++ Monolog/Formatter/JsonFormatter.php | 38 ++ Monolog/Formatter/LineFormatter.php | 90 ++++ Monolog/Formatter/NormalizerFormatter.php | 92 ++++ Monolog/Formatter/WildfireFormatter.php | 99 ++++ Monolog/Handler/AbstractHandler.php | 174 +++++++ Monolog/Handler/AbstractProcessingHandler.php | 66 +++ Monolog/Handler/AmqpHandler.php | 69 +++ Monolog/Handler/BufferHandler.php | 73 +++ Monolog/Handler/ChromePHPHandler.php | 126 +++++ .../ActivationStrategyInterface.php | 28 ++ .../ErrorLevelActivationStrategy.php | 32 ++ Monolog/Handler/FingersCrossedHandler.php | 104 ++++ Monolog/Handler/FirePHPHandler.php | 160 ++++++ Monolog/Handler/GelfHandler.php | 66 +++ Monolog/Handler/GroupHandler.php | 74 +++ Monolog/Handler/HandlerInterface.php | 77 +++ Monolog/Handler/MailHandler.php | 55 +++ Monolog/Handler/MongoDBHandler.php | 51 ++ Monolog/Handler/NativeMailerHandler.php | 65 +++ Monolog/Handler/NullHandler.php | 45 ++ Monolog/Handler/RotatingFileHandler.php | 109 ++++ Monolog/Handler/SocketHandler.php | 272 ++++++++++ Monolog/Handler/StreamHandler.php | 71 +++ Monolog/Handler/SwiftMailerHandler.php | 55 +++ Monolog/Handler/SyslogHandler.php | 110 +++++ Monolog/Handler/TestHandler.php | 140 ++++++ Monolog/Logger.php | 466 ++++++++++++++++++ Monolog/Processor/IntrospectionProcessor.php | 58 +++ .../Processor/MemoryPeakUsageProcessor.php | 40 ++ Monolog/Processor/MemoryProcessor.php | 50 ++ Monolog/Processor/MemoryUsageProcessor.php | 40 ++ Monolog/Processor/WebProcessor.php | 66 +++ Trackvia/Api.php | 303 ++++++++++++ Trackvia/Authentication.php | 285 +++++++++++ Trackvia/Log.php | 32 ++ Trackvia/Request.php | 128 +++++ api_sample.php | 93 ++++ 40 files changed, 4111 insertions(+) create mode 100755 Monolog/Formatter/ChromePHPFormatter.php create mode 100755 Monolog/Formatter/FormatterInterface.php create mode 100755 Monolog/Formatter/GelfMessageFormatter.php create mode 100755 Monolog/Formatter/JsonFormatter.php create mode 100755 Monolog/Formatter/LineFormatter.php create mode 100755 Monolog/Formatter/NormalizerFormatter.php create mode 100755 Monolog/Formatter/WildfireFormatter.php create mode 100755 Monolog/Handler/AbstractHandler.php create mode 100755 Monolog/Handler/AbstractProcessingHandler.php create mode 100755 Monolog/Handler/AmqpHandler.php create mode 100755 Monolog/Handler/BufferHandler.php create mode 100755 Monolog/Handler/ChromePHPHandler.php create mode 100755 Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php create mode 100755 Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php create mode 100755 Monolog/Handler/FingersCrossedHandler.php create mode 100755 Monolog/Handler/FirePHPHandler.php create mode 100755 Monolog/Handler/GelfHandler.php create mode 100755 Monolog/Handler/GroupHandler.php create mode 100755 Monolog/Handler/HandlerInterface.php create mode 100755 Monolog/Handler/MailHandler.php create mode 100755 Monolog/Handler/MongoDBHandler.php create mode 100755 Monolog/Handler/NativeMailerHandler.php create mode 100755 Monolog/Handler/NullHandler.php create mode 100755 Monolog/Handler/RotatingFileHandler.php create mode 100755 Monolog/Handler/SocketHandler.php create mode 100755 Monolog/Handler/StreamHandler.php create mode 100755 Monolog/Handler/SwiftMailerHandler.php create mode 100755 Monolog/Handler/SyslogHandler.php create mode 100755 Monolog/Handler/TestHandler.php create mode 100755 Monolog/Logger.php create mode 100755 Monolog/Processor/IntrospectionProcessor.php create mode 100755 Monolog/Processor/MemoryPeakUsageProcessor.php create mode 100755 Monolog/Processor/MemoryProcessor.php create mode 100755 Monolog/Processor/MemoryUsageProcessor.php create mode 100755 Monolog/Processor/WebProcessor.php create mode 100644 Trackvia/Api.php create mode 100644 Trackvia/Authentication.php create mode 100644 Trackvia/Log.php create mode 100644 Trackvia/Request.php create mode 100644 api_sample.php diff --git a/Monolog/Formatter/ChromePHPFormatter.php b/Monolog/Formatter/ChromePHPFormatter.php new file mode 100755 index 0000000..56d3e27 --- /dev/null +++ b/Monolog/Formatter/ChromePHPFormatter.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; + +/** + * Formats a log message according to the ChromePHP array format + * + * @author Christophe Coevoet + */ +class ChromePHPFormatter implements FormatterInterface +{ + /** + * Translates Monolog log levels to Wildfire levels. + */ + private $logLevels = array( + Logger::DEBUG => 'log', + Logger::INFO => 'info', + Logger::NOTICE => 'info', + Logger::WARNING => 'warn', + Logger::ERROR => 'error', + Logger::CRITICAL => 'error', + Logger::ALERT => 'error', + Logger::EMERGENCY => 'error', + ); + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + // Retrieve the line and file if set and remove them from the formatted extra + $backtrace = 'unknown'; + if (isset($record['extra']['file']) && isset($record['extra']['line'])) { + $backtrace = $record['extra']['file'].' : '.$record['extra']['line']; + unset($record['extra']['file']); + unset($record['extra']['line']); + } + + $message = array('message' => $record['message']); + if ($record['context']) { + $message['context'] = $record['context']; + } + if ($record['extra']) { + $message['extra'] = $record['extra']; + } + if (count($message) === 1) { + $message = reset($message); + } + + return array( + $record['channel'], + $message, + $backtrace, + $this->logLevels[$record['level']], + ); + } + + public function formatBatch(array $records) + { + $formatted = array(); + + foreach ($records as $record) { + $formatted[] = $this->format($record); + } + + return $formatted; + } +} diff --git a/Monolog/Formatter/FormatterInterface.php b/Monolog/Formatter/FormatterInterface.php new file mode 100755 index 0000000..b5de751 --- /dev/null +++ b/Monolog/Formatter/FormatterInterface.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * Interface for formatters + * + * @author Jordi Boggiano + */ +interface FormatterInterface +{ + /** + * Formats a log record. + * + * @param array $record A record to format + * @return mixed The formatted record + */ + public function format(array $record); + + /** + * Formats a set of log records. + * + * @param array $records A set of records to format + * @return mixed The formatted set of records + */ + public function formatBatch(array $records); +} diff --git a/Monolog/Formatter/GelfMessageFormatter.php b/Monolog/Formatter/GelfMessageFormatter.php new file mode 100755 index 0000000..0856f86 --- /dev/null +++ b/Monolog/Formatter/GelfMessageFormatter.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; +use Gelf\Message; + +/** + * Serializes a log message to GELF + * @see http://www.graylog2.org/about/gelf + * + * @author Matt Lehner + */ +class GelfMessageFormatter extends NormalizerFormatter +{ + /** + * @var string the name of the system for the Gelf log message + */ + protected $systemName; + + /** + * @var string a prefix for 'extra' fields from the Monolog record (optional) + */ + protected $extraPrefix; + + /** + * @var string a prefix for 'context' fields from the Monolog record (optional) + */ + protected $contextPrefix; + + /** + * Translates Monolog log levels to Graylog2 log priorities. + */ + private $logLevels = array( + Logger::DEBUG => LOG_DEBUG, + Logger::INFO => LOG_INFO, + Logger::NOTICE => LOG_NOTICE, + Logger::WARNING => LOG_WARNING, + Logger::ERROR => LOG_ERR, + Logger::CRITICAL => LOG_CRIT, + Logger::ALERT => LOG_ALERT, + Logger::EMERGENCY => LOG_EMERG, + ); + + public function __construct($systemName = null, $extraPrefix = null, $contextPrefix = 'ctxt_') + { + parent::__construct('U.u'); + + $this->systemName = $systemName ?: gethostname(); + + $this->extraPrefix = $extraPrefix; + $this->contextPrefix = $contextPrefix; + } + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + $record = parent::format($record); + $message = new Message(); + $message + ->setTimestamp($record['datetime']) + ->setShortMessage((string) $record['message']) + ->setFacility($record['channel']) + ->setHost($this->systemName) + ->setLine(isset($record['extra']['line']) ? $record['extra']['line'] : null) + ->setFile(isset($record['extra']['file']) ? $record['extra']['file'] : null) + ->setLevel($this->logLevels[$record['level']]); + + // Do not duplicate these values in the additional fields + unset($record['extra']['line']); + unset($record['extra']['file']); + + foreach ($record['extra'] as $key => $val) { + $message->setAdditional($this->extraPrefix . $key, is_scalar($val) ? $val : $this->toJson($val)); + } + + foreach ($record['context'] as $key => $val) { + $message->setAdditional($this->contextPrefix . $key, is_scalar($val) ? $val : $this->toJson($val)); + } + + return $message; + } +} diff --git a/Monolog/Formatter/JsonFormatter.php b/Monolog/Formatter/JsonFormatter.php new file mode 100755 index 0000000..822af0e --- /dev/null +++ b/Monolog/Formatter/JsonFormatter.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * Encodes whatever record data is passed to it as json + * + * This can be useful to log to databases or remote APIs + * + * @author Jordi Boggiano + */ +class JsonFormatter implements FormatterInterface +{ + /** + * {@inheritdoc} + */ + public function format(array $record) + { + return json_encode($record); + } + + /** + * {@inheritdoc} + */ + public function formatBatch(array $records) + { + return json_encode($records); + } +} diff --git a/Monolog/Formatter/LineFormatter.php b/Monolog/Formatter/LineFormatter.php new file mode 100755 index 0000000..1054dbb --- /dev/null +++ b/Monolog/Formatter/LineFormatter.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * Formats incoming records into a one-line string + * + * This is especially useful for logging to files + * + * @author Jordi Boggiano + * @author Christophe Coevoet + */ +class LineFormatter extends NormalizerFormatter +{ + const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"; + + protected $format; + + /** + * @param string $format The format of the message + * @param string $dateFormat The format of the timestamp: one supported by DateTime::format + */ + public function __construct($format = null, $dateFormat = null) + { + $this->format = $format ?: static::SIMPLE_FORMAT; + parent::__construct($dateFormat); + } + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + $vars = parent::format($record); + + $output = $this->format; + foreach ($vars['extra'] as $var => $val) { + if (false !== strpos($output, '%extra.'.$var.'%')) { + $output = str_replace('%extra.'.$var.'%', $this->convertToString($val), $output); + unset($vars['extra'][$var]); + } + } + foreach ($vars as $var => $val) { + $output = str_replace('%'.$var.'%', $this->convertToString($val), $output); + } + + return $output; + } + + public function formatBatch(array $records) + { + $message = ''; + foreach ($records as $record) { + $message .= $this->format($record); + } + + return $message; + } + + protected function normalize($data) + { + if (is_bool($data) || is_null($data)) { + return var_export($data, true); + } + + return parent::normalize($data); + } + + protected function convertToString($data) + { + if (null === $data || is_scalar($data)) { + return (string) $data; + } + + if (version_compare(PHP_VERSION, '5.4.0', '>=')) { + return json_encode($this->normalize($data), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + } + + return stripslashes(json_encode($this->normalize($data))); + } +} diff --git a/Monolog/Formatter/NormalizerFormatter.php b/Monolog/Formatter/NormalizerFormatter.php new file mode 100755 index 0000000..6ce4a2e --- /dev/null +++ b/Monolog/Formatter/NormalizerFormatter.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * Normalizes incoming records to remove objects/resources so it's easier to dump to various targets + * + * @author Jordi Boggiano + */ +class NormalizerFormatter implements FormatterInterface +{ + const SIMPLE_DATE = "Y-m-d H:i:s"; + + protected $dateFormat; + + /** + * @param string $dateFormat The format of the timestamp: one supported by DateTime::format + */ + public function __construct($dateFormat = null) + { + $this->dateFormat = $dateFormat ?: static::SIMPLE_DATE; + } + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + return $this->normalize($record); + } + + /** + * {@inheritdoc} + */ + public function formatBatch(array $records) + { + foreach ($records as $key => $record) { + $records[$key] = $this->format($record); + } + + return $records; + } + + protected function normalize($data) + { + if (null === $data || is_scalar($data)) { + return $data; + } + + if (is_array($data) || $data instanceof \Traversable) { + $normalized = array(); + + foreach ($data as $key => $value) { + $normalized[$key] = $this->normalize($value); + } + + return $normalized; + } + + if ($data instanceof \DateTime) { + return $data->format($this->dateFormat); + } + + if (is_object($data)) { + return sprintf("[object] (%s: %s)", get_class($data), $this->toJson($data)); + } + + if (is_resource($data)) { + return '[resource]'; + } + + return '[unknown('.gettype($data).')]'; + } + + protected function toJson($data) + { + if (version_compare(PHP_VERSION, '5.4.0', '>=')) { + return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + } + + return json_encode($data); + } +} diff --git a/Monolog/Formatter/WildfireFormatter.php b/Monolog/Formatter/WildfireFormatter.php new file mode 100755 index 0000000..8ca6f2b --- /dev/null +++ b/Monolog/Formatter/WildfireFormatter.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; + +/** + * Serializes a log message according to Wildfire's header requirements + * + * @author Eric Clemmons (@ericclemmons) + * @author Christophe Coevoet + * @author Kirill chEbba Chebunin + */ +class WildfireFormatter extends NormalizerFormatter +{ + /** + * Translates Monolog log levels to Wildfire levels. + */ + private $logLevels = array( + Logger::DEBUG => 'LOG', + Logger::INFO => 'INFO', + Logger::NOTICE => 'INFO', + Logger::WARNING => 'WARN', + Logger::ERROR => 'ERROR', + Logger::CRITICAL => 'ERROR', + Logger::ALERT => 'ERROR', + Logger::EMERGENCY => 'ERROR', + ); + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + // Retrieve the line and file if set and remove them from the formatted extra + $file = $line = ''; + if (isset($record['extra']['file'])) { + $file = $record['extra']['file']; + unset($record['extra']['file']); + } + if (isset($record['extra']['line'])) { + $line = $record['extra']['line']; + unset($record['extra']['line']); + } + + $record = $this->normalize($record); + $message = array('message' => $record['message']); + if ($record['context']) { + $message['context'] = $record['context']; + } + if ($record['extra']) { + $message['extra'] = $record['extra']; + } + if (count($message) === 1) { + $message = reset($message); + } + + // Create JSON object describing the appearance of the message in the console + $json = json_encode(array( + array( + 'Type' => $this->logLevels[$record['level']], + 'File' => $file, + 'Line' => $line, + 'Label' => $record['channel'], + ), + $message, + )); + + // The message itself is a serialization of the above JSON object + it's length + return sprintf( + '%s|%s|', + strlen($json), + $json + ); + } + + public function formatBatch(array $records) + { + throw new \BadMethodCallException('Batch formatting does not make sense for the WildfireFormatter'); + } + + protected function normalize($data) + { + if (is_object($data) && !$data instanceof \DateTime) { + return $data; + } + + return parent::normalize($data); + } +} diff --git a/Monolog/Handler/AbstractHandler.php b/Monolog/Handler/AbstractHandler.php new file mode 100755 index 0000000..2ea9f55 --- /dev/null +++ b/Monolog/Handler/AbstractHandler.php @@ -0,0 +1,174 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LineFormatter; + +/** + * Base Handler class providing the Handler structure + * + * @author Jordi Boggiano + */ +abstract class AbstractHandler implements HandlerInterface +{ + protected $level = Logger::DEBUG; + protected $bubble = false; + + /** + * @var FormatterInterface + */ + protected $formatter; + protected $processors = array(); + + /** + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($level = Logger::DEBUG, $bubble = true) + { + $this->level = $level; + $this->bubble = $bubble; + } + + /** + * {@inheritdoc} + */ + public function isHandling(array $record) + { + return $record['level'] >= $this->level; + } + + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + foreach ($records as $record) { + $this->handle($record); + } + } + + /** + * Closes the handler. + * + * This will be called automatically when the object is destroyed + */ + public function close() + { + } + + /** + * {@inheritdoc} + */ + public function pushProcessor($callback) + { + if (!is_callable($callback)) { + throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), '.var_export($callback, true).' given'); + } + array_unshift($this->processors, $callback); + } + + /** + * {@inheritdoc} + */ + public function popProcessor() + { + if (!$this->processors) { + throw new \LogicException('You tried to pop from an empty processor stack.'); + } + + return array_shift($this->processors); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(FormatterInterface $formatter) + { + $this->formatter = $formatter; + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + if (!$this->formatter) { + $this->formatter = $this->getDefaultFormatter(); + } + + return $this->formatter; + } + + /** + * Sets minimum logging level at which this handler will be triggered. + * + * @param integer $level + */ + public function setLevel($level) + { + $this->level = $level; + } + + /** + * Gets minimum logging level at which this handler will be triggered. + * + * @return integer + */ + public function getLevel() + { + return $this->level; + } + + /** + * Sets the bubbling behavior. + * + * @param Boolean $bubble True means that bubbling is not permitted. + * False means that this handler allows bubbling. + */ + public function setBubble($bubble) + { + $this->bubble = $bubble; + } + + /** + * Gets the bubbling behavior. + * + * @return Boolean True means that bubbling is not permitted. + * False means that this handler allows bubbling. + */ + public function getBubble() + { + return $this->bubble; + } + + public function __destruct() + { + try { + $this->close(); + } catch (\Exception $e) { + // do nothing + } + } + + /** + * Gets the default formatter. + * + * @return FormatterInterface + */ + protected function getDefaultFormatter() + { + return new LineFormatter(); + } +} diff --git a/Monolog/Handler/AbstractProcessingHandler.php b/Monolog/Handler/AbstractProcessingHandler.php new file mode 100755 index 0000000..e1e5b89 --- /dev/null +++ b/Monolog/Handler/AbstractProcessingHandler.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Base Handler class providing the Handler structure + * + * Classes extending it should (in most cases) only implement write($record) + * + * @author Jordi Boggiano + * @author Christophe Coevoet + */ +abstract class AbstractProcessingHandler extends AbstractHandler +{ + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if ($record['level'] < $this->level) { + return false; + } + + $record = $this->processRecord($record); + + $record['formatted'] = $this->getFormatter()->format($record); + + $this->write($record); + + return false === $this->bubble; + } + + /** + * Writes the record down to the log of the implementing handler + * + * @param array $record + * @return void + */ + abstract protected function write(array $record); + + /** + * Processes a record. + * + * @param array $record + * @return array + */ + protected function processRecord(array $record) + { + if ($this->processors) { + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + } + + return $record; + } +} diff --git a/Monolog/Handler/AmqpHandler.php b/Monolog/Handler/AmqpHandler.php new file mode 100755 index 0000000..0070343 --- /dev/null +++ b/Monolog/Handler/AmqpHandler.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\JsonFormatter; + +class AmqpHandler extends AbstractProcessingHandler +{ + /** + * @var \AMQPExchange $exchange + */ + protected $exchange; + + /** + * @param \AMQPExchange $exchange AMQP exchange, ready for use + * @param string $exchangeName + * @param int $level + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(\AMQPExchange $exchange, $exchangeName = 'log', $level = Logger::DEBUG, $bubble = true) + { + $this->exchange = $exchange; + $this->exchange->setName($exchangeName); + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record) + { + $data = $record["formatted"]; + + $routingKey = sprintf( + '%s.%s', + substr($record['level_name'], 0, 4), + $record['channel'] + ); + + $this->exchange->publish( + $data, + strtolower($routingKey), + 0, + array( + 'delivery_mode' => 2, + 'Content-type' => 'application/json' + ) + ); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new JsonFormatter(); + } +} diff --git a/Monolog/Handler/BufferHandler.php b/Monolog/Handler/BufferHandler.php new file mode 100755 index 0000000..afbb4a6 --- /dev/null +++ b/Monolog/Handler/BufferHandler.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Buffers all records until closing the handler and then pass them as batch. + * + * This is useful for a MailHandler to send only one mail per request instead of + * sending one per log message. + * + * @author Christophe Coevoet + */ +class BufferHandler extends AbstractHandler +{ + protected $handler; + protected $bufferSize; + protected $buffer = array(); + + /** + * @param HandlerInterface $handler Handler. + * @param integer $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(HandlerInterface $handler, $bufferSize = 0, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + $this->handler = $handler; + $this->bufferSize = $bufferSize; + + // __destructor() doesn't get called on Fatal errors + register_shutdown_function(array($this, 'close')); + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if ($record['level'] < $this->level) { + return false; + } + + $this->buffer[] = $record; + if ($this->bufferSize > 0 && count($this->buffer) > $this->bufferSize) { + array_shift($this->buffer); + } + + return false === $this->bubble; + } + + /** + * {@inheritdoc} + */ + public function close() + { + if ($this->buffer) { + $this->handler->handleBatch($this->buffer); + $this->buffer = array(); + } + } +} diff --git a/Monolog/Handler/ChromePHPHandler.php b/Monolog/Handler/ChromePHPHandler.php new file mode 100755 index 0000000..86d7feb --- /dev/null +++ b/Monolog/Handler/ChromePHPHandler.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\ChromePHPFormatter; + +/** + * Handler sending logs to the ChromePHP extension (http://www.chromephp.com/) + * + * @author Christophe Coevoet + */ +class ChromePHPHandler extends AbstractProcessingHandler +{ + /** + * Version of the extension + */ + const VERSION = '3.0'; + + /** + * Header name + */ + const HEADER_NAME = 'X-ChromePhp-Data'; + + protected static $initialized = false; + + protected static $json = array( + 'version' => self::VERSION, + 'columns' => array('label', 'log', 'backtrace', 'type'), + 'rows' => array(), + ); + + protected $sendHeaders = true; + + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + $messages = array(); + + foreach ($records as $record) { + if ($record['level'] < $this->level) { + continue; + } + $messages[] = $this->processRecord($record); + } + + if (!empty($messages)) { + $messages = $this->getFormatter()->formatBatch($messages); + self::$json['rows'] = array_merge(self::$json['rows'], $messages); + $this->send(); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new ChromePHPFormatter(); + } + + /** + * Creates & sends header for a record + * + * @see sendHeader() + * @see send() + * @param array $record + */ + protected function write(array $record) + { + self::$json['rows'][] = $record['formatted']; + + $this->send(); + } + + /** + * Sends the log header + * + * @see sendHeader() + */ + protected function send() + { + if (!self::$initialized) { + $this->sendHeaders = $this->headersAccepted(); + self::$json['request_uri'] = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; + + self::$initialized = true; + } + + $this->sendHeader(self::HEADER_NAME, base64_encode(utf8_encode(json_encode(self::$json)))); + } + + /** + * Send header string to the client + * + * @param string $header + * @param string $content + */ + protected function sendHeader($header, $content) + { + if (!headers_sent() && $this->sendHeaders) { + header(sprintf('%s: %s', $header, $content)); + } + } + + /** + * Verifies if the headers are accepted by the current user agent + * + * @return Boolean + */ + protected function headersAccepted() + { + return !isset($_SERVER['HTTP_USER_AGENT']) + || preg_match('{\bChrome/\d+[\.\d+]*\b}', $_SERVER['HTTP_USER_AGENT']); + } +} diff --git a/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php b/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php new file mode 100755 index 0000000..c3e42ef --- /dev/null +++ b/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\FingersCrossed; + +/** + * Interface for activation strategies for the FingersCrossedHandler. + * + * @author Johannes M. Schmitt + */ +interface ActivationStrategyInterface +{ + /** + * Returns whether the given record activates the handler. + * + * @param array $record + * @return Boolean + */ + public function isHandlerActivated(array $record); +} diff --git a/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php b/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php new file mode 100755 index 0000000..7cd8ef1 --- /dev/null +++ b/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\FingersCrossed; + +/** + * Error level based activation strategy. + * + * @author Johannes M. Schmitt + */ +class ErrorLevelActivationStrategy implements ActivationStrategyInterface +{ + private $actionLevel; + + public function __construct($actionLevel) + { + $this->actionLevel = $actionLevel; + } + + public function isHandlerActivated(array $record) + { + return $record['level'] >= $this->actionLevel; + } +} diff --git a/Monolog/Handler/FingersCrossedHandler.php b/Monolog/Handler/FingersCrossedHandler.php new file mode 100755 index 0000000..561ee7c --- /dev/null +++ b/Monolog/Handler/FingersCrossedHandler.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; +use Monolog\Handler\FingersCrossed\ActivationStrategyInterface; +use Monolog\Logger; + +/** + * Buffers all records until a certain level is reached + * + * The advantage of this approach is that you don't get any clutter in your log files. + * Only requests which actually trigger an error (or whatever your actionLevel is) will be + * in the logs, but they will contain all records, not only those above the level threshold. + * + * @author Jordi Boggiano + */ +class FingersCrossedHandler extends AbstractHandler +{ + protected $handler; + protected $activationStrategy; + protected $buffering = true; + protected $bufferSize; + protected $buffer = array(); + protected $stopBuffering; + + /** + * @param callback|HandlerInterface $handler Handler or factory callback($record, $fingersCrossedHandler). + * @param int|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action + * @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param Boolean $stopBuffering Whether the handler should stop buffering after being triggered (default true) + */ + public function __construct($handler, $activationStrategy = null, $bufferSize = 0, $bubble = true, $stopBuffering = true) + { + if (null === $activationStrategy) { + $activationStrategy = new ErrorLevelActivationStrategy(Logger::WARNING); + } + if (!$activationStrategy instanceof ActivationStrategyInterface) { + $activationStrategy = new ErrorLevelActivationStrategy($activationStrategy); + } + + $this->handler = $handler; + $this->activationStrategy = $activationStrategy; + $this->bufferSize = $bufferSize; + $this->bubble = $bubble; + $this->stopBuffering = $stopBuffering; + } + + /** + * {@inheritdoc} + */ + public function isHandling(array $record) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if ($this->buffering) { + $this->buffer[] = $record; + if ($this->bufferSize > 0 && count($this->buffer) > $this->bufferSize) { + array_shift($this->buffer); + } + if ($this->activationStrategy->isHandlerActivated($record)) { + if ($this->stopBuffering) { + $this->buffering = false; + } + if (!$this->handler instanceof HandlerInterface) { + $this->handler = call_user_func($this->handler, $record, $this); + } + if (!$this->handler instanceof HandlerInterface) { + throw new \RuntimeException("The factory callback should return a HandlerInterface"); + } + $this->handler->handleBatch($this->buffer); + $this->buffer = array(); + } + } else { + $this->handler->handle($record); + } + + return false === $this->bubble; + } + + /** + * Resets the state of the handler. Stops forwarding records to the wrapped handler. + */ + public function reset() + { + $this->buffering = true; + } +} diff --git a/Monolog/Handler/FirePHPHandler.php b/Monolog/Handler/FirePHPHandler.php new file mode 100755 index 0000000..0909218 --- /dev/null +++ b/Monolog/Handler/FirePHPHandler.php @@ -0,0 +1,160 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\WildfireFormatter; + +/** + * Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol. + * + * @author Eric Clemmons (@ericclemmons) + */ +class FirePHPHandler extends AbstractProcessingHandler +{ + /** + * WildFire JSON header message format + */ + const PROTOCOL_URI = 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2'; + + /** + * FirePHP structure for parsing messages & their presentation + */ + const STRUCTURE_URI = 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1'; + + /** + * Must reference a "known" plugin, otherwise headers won't display in FirePHP + */ + const PLUGIN_URI = 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3'; + + /** + * Header prefix for Wildfire to recognize & parse headers + */ + const HEADER_PREFIX = 'X-Wf'; + + /** + * Whether or not Wildfire vendor-specific headers have been generated & sent yet + */ + protected static $initialized = false; + + /** + * Shared static message index between potentially multiple handlers + * @var int + */ + protected static $messageIndex = 1; + + protected $sendHeaders = true; + + /** + * Base header creation function used by init headers & record headers + * + * @param array $meta Wildfire Plugin, Protocol & Structure Indexes + * @param string $message Log message + * @return array Complete header string ready for the client as key and message as value + */ + protected function createHeader(array $meta, $message) + { + $header = sprintf('%s-%s', self::HEADER_PREFIX, join('-', $meta)); + + return array($header => $message); + } + + /** + * Creates message header from record + * + * @see createHeader() + * @param array $record + * @return string + */ + protected function createRecordHeader(array $record) + { + // Wildfire is extensible to support multiple protocols & plugins in a single request, + // but we're not taking advantage of that (yet), so we're using "1" for simplicity's sake. + return $this->createHeader( + array(1, 1, 1, self::$messageIndex++), + $record['formatted'] + ); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new WildfireFormatter(); + } + + /** + * Wildfire initialization headers to enable message parsing + * + * @see createHeader() + * @see sendHeader() + * @return array + */ + protected function getInitHeaders() + { + // Initial payload consists of required headers for Wildfire + return array_merge( + $this->createHeader(array('Protocol', 1), self::PROTOCOL_URI), + $this->createHeader(array(1, 'Structure', 1), self::STRUCTURE_URI), + $this->createHeader(array(1, 'Plugin', 1), self::PLUGIN_URI) + ); + } + + /** + * Send header string to the client + * + * @param string $header + * @param string $content + */ + protected function sendHeader($header, $content) + { + if (!headers_sent() && $this->sendHeaders) { + header(sprintf('%s: %s', $header, $content)); + } + } + + /** + * Creates & sends header for a record, ensuring init headers have been sent prior + * + * @see sendHeader() + * @see sendInitHeaders() + * @param array $record + */ + protected function write(array $record) + { + // WildFire-specific headers must be sent prior to any messages + if (!self::$initialized) { + $this->sendHeaders = $this->headersAccepted(); + + foreach ($this->getInitHeaders() as $header => $content) { + $this->sendHeader($header, $content); + } + + self::$initialized = true; + } + + $header = $this->createRecordHeader($record); + $this->sendHeader(key($header), current($header)); + } + + /** + * Verifies if the headers are accepted by the current user agent + * + * @return Boolean + */ + protected function headersAccepted() + { + return !isset($_SERVER['HTTP_USER_AGENT']) + || preg_match('{\bFirePHP/\d+\.\d+\b}', $_SERVER['HTTP_USER_AGENT']) + || isset($_SERVER['HTTP_X_FIREPHP_VERSION']); + } +} diff --git a/Monolog/Handler/GelfHandler.php b/Monolog/Handler/GelfHandler.php new file mode 100755 index 0000000..34d48e7 --- /dev/null +++ b/Monolog/Handler/GelfHandler.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Gelf\IMessagePublisher; +use Monolog\Logger; +use Monolog\Handler\AbstractProcessingHandler; +use Monolog\Formatter\GelfMessageFormatter; + +/** + * Handler to send messages to a Graylog2 (http://www.graylog2.org) server + * + * @author Matt Lehner + */ +class GelfHandler extends AbstractProcessingHandler +{ + /** + * @var Gelf\IMessagePublisher the publisher object that sends the message to the server + */ + protected $publisher; + + /** + * @param Gelf\IMessagePublisher $publisher a publisher object + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(IMessagePublisher $publisher, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + + $this->publisher = $publisher; + } + + /** + * {@inheritdoc} + */ + public function close() + { + $this->publisher = null; + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + $this->publisher->publish($record['formatted']); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new GelfMessageFormatter(); + } +} diff --git a/Monolog/Handler/GroupHandler.php b/Monolog/Handler/GroupHandler.php new file mode 100755 index 0000000..cd29531 --- /dev/null +++ b/Monolog/Handler/GroupHandler.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Forwards records to multiple handlers + * + * @author Lenar Lõhmus + */ +class GroupHandler extends AbstractHandler +{ + protected $handlers; + + /** + * @param array $handlers Array of Handlers. + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(array $handlers, $bubble = true) + { + foreach ($handlers as $handler) { + if (!$handler instanceof HandlerInterface) { + throw new \InvalidArgumentException('The first argument of the GroupHandler must be an array of HandlerInterface instances.'); + } + } + + $this->handlers = $handlers; + $this->bubble = $bubble; + } + + /** + * {@inheritdoc} + */ + public function isHandling(array $record) + { + foreach ($this->handlers as $handler) { + if ($handler->isHandling($record)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + foreach ($this->handlers as $handler) { + $handler->handle($record); + } + + return false === $this->bubble; + } + + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + foreach ($this->handlers as $handler) { + $handler->handleBatch($records); + } + } +} diff --git a/Monolog/Handler/HandlerInterface.php b/Monolog/Handler/HandlerInterface.php new file mode 100755 index 0000000..d24dc77 --- /dev/null +++ b/Monolog/Handler/HandlerInterface.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; + +/** + * Interface that all Monolog Handlers must implement + * + * @author Jordi Boggiano + */ +interface HandlerInterface +{ + /** + * Checks whether the given record will be handled by this handler. + * + * This is mostly done for performance reasons, to avoid calling processors for nothing. + * + * @return Boolean + */ + public function isHandling(array $record); + + /** + * Handles a record. + * + * The return value of this function controls the bubbling process of the handler stack. + * + * @param array $record The record to handle + * @return Boolean True means that this handler handled the record, and that bubbling is not permitted. + * False means the record was either not processed or that this handler allows bubbling. + */ + public function handle(array $record); + + /** + * Handles a set of records at once. + * + * @param array $records The records to handle (an array of record arrays) + */ + public function handleBatch(array $records); + + /** + * Adds a processor in the stack. + * + * @param callable $callback + */ + public function pushProcessor($callback); + + /** + * Removes the processor on top of the stack and returns it. + * + * @return callable + */ + public function popProcessor(); + + /** + * Sets the formatter. + * + * @param FormatterInterface $formatter + */ + public function setFormatter(FormatterInterface $formatter); + + /** + * Gets the formatter. + * + * @return FormatterInterface + */ + public function getFormatter(); +} diff --git a/Monolog/Handler/MailHandler.php b/Monolog/Handler/MailHandler.php new file mode 100755 index 0000000..8629272 --- /dev/null +++ b/Monolog/Handler/MailHandler.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Base class for all mail handlers + * + * @author Gyula Sallai + */ +abstract class MailHandler extends AbstractProcessingHandler +{ + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + $messages = array(); + + foreach ($records as $record) { + if ($record['level'] < $this->level) { + continue; + } + $messages[] = $this->processRecord($record); + } + + if (!empty($messages)) { + $this->send((string) $this->getFormatter()->formatBatch($messages), $messages); + } + } + + /** + * Send a mail with the given content + * + * @param string $content + * @param array $records the array of log records that formed this content + */ + abstract protected function send($content, array $records); + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + $this->send((string) $record['formatted'], array($record)); + } +} diff --git a/Monolog/Handler/MongoDBHandler.php b/Monolog/Handler/MongoDBHandler.php new file mode 100755 index 0000000..210bb19 --- /dev/null +++ b/Monolog/Handler/MongoDBHandler.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\NormalizerFormatter; + +/** + * Logs to a MongoDB database. + * + * usage example: + * + * $log = new Logger('application'); + * $mongodb = new MongoDBHandler(new \Mongo("mongodb://localhost:27017"), "logs", "prod"); + * $log->pushHandler($mongodb); + * + * @author Thomas Tourlourat + */ +class MongoDBHandler extends AbstractProcessingHandler +{ + private $mongoCollection; + + public function __construct(\Mongo $mongo, $database, $collection, $level = Logger::DEBUG, $bubble = true) + { + $this->mongoCollection = $mongo->selectCollection($database, $collection); + + parent::__construct($level, $bubble); + } + + protected function write(array $record) + { + $this->mongoCollection->save($record["formatted"]); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new NormalizerFormatter(); + } +} diff --git a/Monolog/Handler/NativeMailerHandler.php b/Monolog/Handler/NativeMailerHandler.php new file mode 100755 index 0000000..c954de5 --- /dev/null +++ b/Monolog/Handler/NativeMailerHandler.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * NativeMailerHandler uses the mail() function to send the emails + * + * @author Christophe Coevoet + */ +class NativeMailerHandler extends MailHandler +{ + protected $to; + protected $subject; + protected $headers = array( + 'Content-type: text/plain; charset=utf-8' + ); + + /** + * @param string|array $to The receiver of the mail + * @param string $subject The subject of the mail + * @param string $from The sender of the mail + * @param integer $level The minimum logging level at which this handler will be triggered + * @param boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($to, $subject, $from, $level = Logger::ERROR, $bubble = true) + { + parent::__construct($level, $bubble); + $this->to = is_array($to) ? $to : array($to); + $this->subject = $subject; + $this->headers[] = sprintf('From: %s', $from); + } + + /** + * @param string|array $header Custom added headers + */ + public function addHeader($headers) + { + if (is_array($headers)) { + $this->headers = array_merge($this->headers, $headers); + } else { + $this->headers[] = $headers; + } + } + + /** + * {@inheritdoc} + */ + protected function send($content, array $records) + { + foreach ($this->to as $to) { + mail($to, $this->subject, wordwrap($content, 70), implode("\r\n", $this->headers) . "\r\n"); + } + } +} diff --git a/Monolog/Handler/NullHandler.php b/Monolog/Handler/NullHandler.php new file mode 100755 index 0000000..3754e45 --- /dev/null +++ b/Monolog/Handler/NullHandler.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Blackhole + * + * Any record it can handle will be thrown away. This can be used + * to put on top of an existing stack to override it temporarily. + * + * @author Jordi Boggiano + */ +class NullHandler extends AbstractHandler +{ + /** + * @param integer $level The minimum logging level at which this handler will be triggered + */ + public function __construct($level = Logger::DEBUG) + { + parent::__construct($level, false); + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if ($record['level'] < $this->level) { + return false; + } + + return true; + } +} diff --git a/Monolog/Handler/RotatingFileHandler.php b/Monolog/Handler/RotatingFileHandler.php new file mode 100755 index 0000000..682542c --- /dev/null +++ b/Monolog/Handler/RotatingFileHandler.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Stores logs to files that are rotated every day and a limited number of files are kept. + * + * This rotation is only intended to be used as a workaround. Using logrotate to + * handle the rotation is strongly encouraged when you can use it. + * + * @author Christophe Coevoet + */ +class RotatingFileHandler extends StreamHandler +{ + protected $filename; + protected $maxFiles; + protected $mustRotate; + + /** + * @param string $filename + * @param integer $maxFiles The maximal amount of files to keep (0 means unlimited) + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($filename, $maxFiles = 0, $level = Logger::DEBUG, $bubble = true) + { + $this->filename = $filename; + $this->maxFiles = (int) $maxFiles; + + $fileInfo = pathinfo($this->filename); + $timedFilename = $fileInfo['dirname'].'/'.$fileInfo['filename'].'-'.date('Y-m-d'); + if (!empty($fileInfo['extension'])) { + $timedFilename .= '.'.$fileInfo['extension']; + } + + // disable rotation upfront if files are unlimited + if (0 === $this->maxFiles) { + $this->mustRotate = false; + } + + parent::__construct($timedFilename, $level, $bubble); + } + + /** + * {@inheritdoc} + */ + public function close() + { + parent::close(); + + if (true === $this->mustRotate) { + $this->rotate(); + } + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + // on the first record written, if the log is new, we should rotate (once per day) + if (null === $this->mustRotate) { + $this->mustRotate = !file_exists($this->url); + } + + parent::write($record); + } + + /** + * Rotates the files. + */ + protected function rotate() + { + $fileInfo = pathinfo($this->filename); + $glob = $fileInfo['dirname'].'/'.$fileInfo['filename'].'-*'; + if (!empty($fileInfo['extension'])) { + $glob .= '.'.$fileInfo['extension']; + } + $iterator = new \GlobIterator($glob); + $count = $iterator->count(); + if ($this->maxFiles >= $count) { + // no files to remove + return; + } + + // Sorting the files by name to remove the older ones + $array = iterator_to_array($iterator); + usort($array, function($a, $b) { + return strcmp($b->getFilename(), $a->getFilename()); + }); + + foreach (array_slice($array, $this->maxFiles) as $file) { + if ($file->isWritable()) { + unlink($file->getRealPath()); + } + } + } +} diff --git a/Monolog/Handler/SocketHandler.php b/Monolog/Handler/SocketHandler.php new file mode 100755 index 0000000..b44fad7 --- /dev/null +++ b/Monolog/Handler/SocketHandler.php @@ -0,0 +1,272 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Stores to any socket - uses fsockopen() or pfsockopen(). + * + * @author Pablo de Leon Belloc + * @see http://php.net/manual/en/function.fsockopen.php + */ +class SocketHandler extends AbstractProcessingHandler +{ + private $connectionString; + private $connectionTimeout; + private $resource; + private $timeout = 0; + private $persistent = false; + private $errno; + private $errstr; + + /** + * @param string $connectionString Socket connection string + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($connectionString, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + $this->connectionString = $connectionString; + $this->connectionTimeout = (float) ini_get('default_socket_timeout'); + } + + /** + * Connect (if necessary) and write to the socket + * + * @param array $record + * + * @throws \UnexpectedValueException + * @throws \RuntimeException + */ + public function write(array $record) + { + $this->connectIfNotConnected(); + $this->writeToSocket((string) $record['formatted']); + } + + /** + * We will not close a PersistentSocket instance so it can be reused in other requests. + */ + public function close() + { + if (!$this->isPersistent()) { + $this->closeSocket(); + } + } + + /** + * Close socket, if open + */ + public function closeSocket() + { + if (is_resource($this->resource)) { + fclose($this->resource); + $this->resource = null; + } + } + + /** + * Set socket connection to nbe persistent. It only has effect before the connection is initiated. + * + * @param type $boolean + */ + public function setPersistent($boolean) + { + $this->persistent = (boolean) $boolean; + } + + /** + * Set connection timeout. Only has effect before we connect. + * + * @param integer $seconds + * + * @see http://php.net/manual/en/function.fsockopen.php + */ + public function setConnectionTimeout($seconds) + { + $this->validateTimeout($seconds); + $this->connectionTimeout = (float) $seconds; + } + + /** + * Set write timeout. Only has effect before we connect. + * + * @param type $seconds + * + * @see http://php.net/manual/en/function.stream-set-timeout.php + */ + public function setTimeout($seconds) + { + $this->validateTimeout($seconds); + $this->timeout = (int) $seconds; + } + + /** + * Get current connection string + * + * @return string + */ + public function getConnectionString() + { + return $this->connectionString; + } + + /** + * Get persistent setting + * + * @return boolean + */ + public function isPersistent() + { + return $this->persistent; + } + + /** + * Get current connection timeout setting + * + * @return float + */ + public function getConnectionTimeout() + { + return $this->connectionTimeout; + } + + /** + * Get current in-transfer timeout + * + * @return float + */ + public function getTimeout() + { + return $this->timeout; + } + + /** + * Check to see if the socket is currently available. + * + * UDP might appear to be connected but might fail when writing. See http://php.net/fsockopen for details. + * + * @return boolean + */ + public function isConnected() + { + return is_resource($this->resource) + && !feof($this->resource); // on TCP - other party can close connection. + } + + /** + * Wrapper to allow mocking + */ + protected function pfsockopen() + { + return @pfsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); + } + + /** + * Wrapper to allow mocking + */ + protected function fsockopen() + { + return @fsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); + } + + /** + * Wrapper to allow mocking + */ + protected function streamSetTimeout() + { + return stream_set_timeout($this->resource, $this->timeout); + } + + /** + * Wrapper to allow mocking + */ + protected function fwrite($data) + { + return @fwrite($this->resource, $data); + } + + /** + * Wrapper to allow mocking + */ + protected function streamGetMetadata() + { + return stream_get_meta_data($this->resource); + } + + private function validateTimeout($value) + { + $ok = filter_var($value, FILTER_VALIDATE_INT, array('options' => array( + 'min_range' => 0, + ))); + if ($ok === false) { + throw new \InvalidArgumentException("Timeout must be 0 or a positive integer (got $value)"); + } + } + + private function connectIfNotConnected() + { + if ($this->isConnected()) { + return; + } + $this->connect(); + } + + private function connect() + { + $this->createSocketResource(); + $this->setSocketTimeout(); + } + + private function createSocketResource() + { + if ($this->isPersistent()) { + $resource = $this->pfsockopen(); + } else { + $resource = $this->fsockopen(); + } + if (!$resource) { + throw new \UnexpectedValueException("Failed connecting to $this->connectionString ($this->errno: $this->errstr)"); + } + $this->resource = $resource; + } + + private function setSocketTimeout() + { + if (!$this->streamSetTimeout()) { + throw new \UnexpectedValueException("Failed setting timeout with stream_set_timeout()"); + } + } + + private function writeToSocket($data) + { + $length = strlen($data); + $sent = 0; + while ($this->isConnected() && $sent < $length) { + $chunk = $this->fwrite(substr($data, $sent)); + if ($chunk === false) { + throw new \RuntimeException("Could not write to socket"); + } + $sent += $chunk; + $socketInfo = $this->streamGetMetadata(); + if ($socketInfo['timed_out']) { + throw new \RuntimeException("Write timed-out"); + } + } + if (!$this->isConnected() && $sent < $length) { + throw new \RuntimeException("End-of-file reached, probably we got disconnected (sent $sent of $length)"); + } + } + +} diff --git a/Monolog/Handler/StreamHandler.php b/Monolog/Handler/StreamHandler.php new file mode 100755 index 0000000..a437030 --- /dev/null +++ b/Monolog/Handler/StreamHandler.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Stores to any stream resource + * + * Can be used to store into php://stderr, remote and local files, etc. + * + * @author Jordi Boggiano + */ +class StreamHandler extends AbstractProcessingHandler +{ + protected $stream; + protected $url; + + /** + * @param string $stream + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($stream, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + if (is_resource($stream)) { + $this->stream = $stream; + } else { + $this->url = $stream; + } + } + + /** + * {@inheritdoc} + */ + public function close() + { + if (is_resource($this->stream)) { + fclose($this->stream); + } + $this->stream = null; + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + if (null === $this->stream) { + if (!$this->url) { + throw new \LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().'); + } + $this->stream = @fopen($this->url, 'a'); + if (!is_resource($this->stream)) { + $this->stream = null; + throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened; it may be invalid or not writable.', $this->url)); + } + } + fwrite($this->stream, (string) $record['formatted']); + } +} diff --git a/Monolog/Handler/SwiftMailerHandler.php b/Monolog/Handler/SwiftMailerHandler.php new file mode 100755 index 0000000..56bf9a2 --- /dev/null +++ b/Monolog/Handler/SwiftMailerHandler.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * SwiftMailerHandler uses Swift_Mailer to send the emails + * + * @author Gyula Sallai + */ +class SwiftMailerHandler extends MailHandler +{ + protected $mailer; + protected $message; + + /** + * @param \Swift_Mailer $mailer The mailer to use + * @param callback|\Swift_Message $message An example message for real messages, only the body will be replaced + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(\Swift_Mailer $mailer, $message, $level = Logger::ERROR, $bubble = true) + { + parent::__construct($level, $bubble); + $this->mailer = $mailer; + if (!$message instanceof \Swift_Message && is_callable($message)) { + $message = call_user_func($message); + } + if (!$message instanceof \Swift_Message) { + throw new \InvalidArgumentException('You must provide either a Swift_Message instance or a callback returning it'); + } + $this->message = $message; + } + + /** + * {@inheritdoc} + */ + protected function send($content, array $records) + { + $message = clone $this->message; + $message->setBody($content); + + $this->mailer->send($message); + } +} diff --git a/Monolog/Handler/SyslogHandler.php b/Monolog/Handler/SyslogHandler.php new file mode 100755 index 0000000..ed2fe44 --- /dev/null +++ b/Monolog/Handler/SyslogHandler.php @@ -0,0 +1,110 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Logs to syslog service. + * + * usage example: + * + * $log = new Logger('application'); + * $syslog = new SyslogHandler('myfacility', 'local6'); + * $formatter = new LineFormatter("%channel%.%level_name%: %message% %extra%"); + * $syslog->setFormatter($formatter); + * $log->pushHandler($syslog); + * + * @author Sven Paulus + */ +class SyslogHandler extends AbstractProcessingHandler +{ + /** + * Translates Monolog log levels to syslog log priorities. + */ + private $logLevels = array( + Logger::DEBUG => LOG_DEBUG, + Logger::INFO => LOG_INFO, + Logger::NOTICE => LOG_NOTICE, + Logger::WARNING => LOG_WARNING, + Logger::ERROR => LOG_ERR, + Logger::CRITICAL => LOG_CRIT, + Logger::ALERT => LOG_ALERT, + Logger::EMERGENCY => LOG_EMERG, + ); + + /** + * List of valid log facility names. + */ + private $facilities = array( + 'auth' => LOG_AUTH, + 'authpriv' => LOG_AUTHPRIV, + 'cron' => LOG_CRON, + 'daemon' => LOG_DAEMON, + 'kern' => LOG_KERN, + 'lpr' => LOG_LPR, + 'mail' => LOG_MAIL, + 'news' => LOG_NEWS, + 'syslog' => LOG_SYSLOG, + 'user' => LOG_USER, + 'uucp' => LOG_UUCP, + ); + + /** + * @param string $ident + * @param mixed $facility + * @param integer $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($ident, $facility = LOG_USER, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + + if (!defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->facilities['local0'] = LOG_LOCAL0; + $this->facilities['local1'] = LOG_LOCAL1; + $this->facilities['local2'] = LOG_LOCAL2; + $this->facilities['local3'] = LOG_LOCAL3; + $this->facilities['local4'] = LOG_LOCAL4; + $this->facilities['local5'] = LOG_LOCAL5; + $this->facilities['local6'] = LOG_LOCAL6; + $this->facilities['local7'] = LOG_LOCAL7; + } + + // convert textual description of facility to syslog constant + if (array_key_exists(strtolower($facility), $this->facilities)) { + $facility = $this->facilities[strtolower($facility)]; + } elseif (!in_array($facility, array_values($this->facilities), true)) { + throw new \UnexpectedValueException('Unknown facility value "'.$facility.'" given'); + } + + if (!openlog($ident, LOG_PID, $facility)) { + throw new \LogicException('Can\'t open syslog for ident "'.$ident.'" and facility "'.$facility.'"'); + } + } + + /** + * {@inheritdoc} + */ + public function close() + { + closelog(); + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + syslog($this->logLevels[$record['level']], (string) $record['formatted']); + } +} diff --git a/Monolog/Handler/TestHandler.php b/Monolog/Handler/TestHandler.php new file mode 100755 index 0000000..085d9e1 --- /dev/null +++ b/Monolog/Handler/TestHandler.php @@ -0,0 +1,140 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Used for testing purposes. + * + * It records all records and gives you access to them for verification. + * + * @author Jordi Boggiano + */ +class TestHandler extends AbstractProcessingHandler +{ + protected $records = array(); + protected $recordsByLevel = array(); + + public function getRecords() + { + return $this->records; + } + + public function hasEmergency($record) + { + return $this->hasRecord($record, Logger::EMERGENCY); + } + + public function hasAlert($record) + { + return $this->hasRecord($record, Logger::ALERT); + } + + public function hasCritical($record) + { + return $this->hasRecord($record, Logger::CRITICAL); + } + + public function hasError($record) + { + return $this->hasRecord($record, Logger::ERROR); + } + + public function hasWarning($record) + { + return $this->hasRecord($record, Logger::WARNING); + } + + public function hasNotice($record) + { + return $this->hasRecord($record, Logger::NOTICE); + } + + public function hasInfo($record) + { + return $this->hasRecord($record, Logger::INFO); + } + + public function hasDebug($record) + { + return $this->hasRecord($record, Logger::DEBUG); + } + + public function hasEmergencyRecords() + { + return isset($this->recordsByLevel[Logger::EMERGENCY]); + } + + public function hasAlertRecords() + { + return isset($this->recordsByLevel[Logger::ALERT]); + } + + public function hasCriticalRecords() + { + return isset($this->recordsByLevel[Logger::CRITICAL]); + } + + public function hasErrorRecords() + { + return isset($this->recordsByLevel[Logger::ERROR]); + } + + public function hasWarningRecords() + { + return isset($this->recordsByLevel[Logger::WARNING]); + } + + public function hasNoticeRecords() + { + return isset($this->recordsByLevel[Logger::NOTICE]); + } + + public function hasInfoRecords() + { + return isset($this->recordsByLevel[Logger::INFO]); + } + + public function hasDebugRecords() + { + return isset($this->recordsByLevel[Logger::DEBUG]); + } + + protected function hasRecord($record, $level) + { + if (!isset($this->recordsByLevel[$level])) { + return false; + } + + if (is_array($record)) { + $record = $record['message']; + } + + foreach ($this->recordsByLevel[$level] as $rec) { + if ($rec['message'] === $record) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + $this->recordsByLevel[$record['level']][] = $record; + $this->records[] = $record; + } +} diff --git a/Monolog/Logger.php b/Monolog/Logger.php new file mode 100755 index 0000000..0ff2aa8 --- /dev/null +++ b/Monolog/Logger.php @@ -0,0 +1,466 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use Monolog\Handler\HandlerInterface; +use Monolog\Handler\StreamHandler; + +/** + * Monolog log channel + * + * It contains a stack of Handlers and a stack of Processors, + * and uses them to store records that are added to it. + * + * @author Jordi Boggiano + */ +class Logger +{ + /** + * Detailed debug information + */ + const DEBUG = 100; + + /** + * Interesting events + * + * Examples: User logs in, SQL logs. + */ + const INFO = 200; + + /** + * Uncommon events + */ + const NOTICE = 250; + + /** + * Exceptional occurrences that are not errors + * + * Examples: Use of deprecated APIs, poor use of an API, + * undesirable things that are not necessarily wrong. + */ + const WARNING = 300; + + /** + * Runtime errors + */ + const ERROR = 400; + + /** + * Critical conditions + * + * Example: Application component unavailable, unexpected exception. + */ + const CRITICAL = 500; + + /** + * Action must be taken immediately + * + * Example: Entire website down, database unavailable, etc. + * This should trigger the SMS alerts and wake you up. + */ + const ALERT = 550; + + /** + * Urgent alert. + */ + const EMERGENCY = 600; + + protected static $levels = array( + 100 => 'DEBUG', + 200 => 'INFO', + 250 => 'NOTICE', + 300 => 'WARNING', + 400 => 'ERROR', + 500 => 'CRITICAL', + 550 => 'ALERT', + 600 => 'EMERGENCY', + ); + + /** + * @var DateTimeZone + */ + protected static $timezone; + + protected $name; + + /** + * The handler stack + * + * @var array of Monolog\Handler\HandlerInterface + */ + protected $handlers = array(); + + protected $processors = array(); + + /** + * @param string $name The logging channel + */ + public function __construct($name) + { + $this->name = $name; + + if (!self::$timezone) { + self::$timezone = new \DateTimeZone(date_default_timezone_get() ?: 'UTC'); + } + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Pushes a handler on to the stack. + * + * @param HandlerInterface $handler + */ + public function pushHandler(HandlerInterface $handler) + { + array_unshift($this->handlers, $handler); + } + + /** + * Pops a handler from the stack + * + * @return HandlerInterface + */ + public function popHandler() + { + if (!$this->handlers) { + throw new \LogicException('You tried to pop from an empty handler stack.'); + } + + return array_shift($this->handlers); + } + + /** + * Adds a processor on to the stack. + * + * @param callable $callback + */ + public function pushProcessor($callback) + { + if (!is_callable($callback)) { + throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), '.var_export($callback, true).' given'); + } + array_unshift($this->processors, $callback); + } + + /** + * Removes the processor on top of the stack and returns it. + * + * @return callable + */ + public function popProcessor() + { + if (!$this->processors) { + throw new \LogicException('You tried to pop from an empty processor stack.'); + } + + return array_shift($this->processors); + } + + /** + * Adds a log record. + * + * @param integer $level The logging level + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addRecord($level, $message, array $context = array()) + { + if (!$this->handlers) { + $this->pushHandler(new StreamHandler('php://stderr', self::DEBUG)); + } + $record = array( + 'message' => (string) $message, + 'context' => $context, + 'level' => $level, + 'level_name' => self::getLevelName($level), + 'channel' => $this->name, + 'datetime' => \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true)))->setTimeZone(self::$timezone), + 'extra' => array(), + ); + // check if any message will handle this message + $handlerKey = null; + foreach ($this->handlers as $key => $handler) { + if ($handler->isHandling($record)) { + $handlerKey = $key; + break; + } + } + // none found + if (null === $handlerKey) { + return false; + } + // found at least one, process message and dispatch it + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + while (isset($this->handlers[$handlerKey]) && + false === $this->handlers[$handlerKey]->handle($record)) { + $handlerKey++; + } + + return true; + } + + /** + * Adds a log record at the DEBUG level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addDebug($message, array $context = array()) + { + return $this->addRecord(self::DEBUG, $message, $context); + } + + /** + * Adds a log record at the INFO level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addInfo($message, array $context = array()) + { + return $this->addRecord(self::INFO, $message, $context); + } + + /** + * Adds a log record at the NOTICE level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addNotice($message, array $context = array()) + { + return $this->addRecord(self::NOTICE, $message, $context); + } + + /** + * Adds a log record at the WARNING level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addWarning($message, array $context = array()) + { + return $this->addRecord(self::WARNING, $message, $context); + } + + /** + * Adds a log record at the ERROR level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addError($message, array $context = array()) + { + return $this->addRecord(self::ERROR, $message, $context); + } + + /** + * Adds a log record at the CRITICAL level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addCritical($message, array $context = array()) + { + return $this->addRecord(self::CRITICAL, $message, $context); + } + + /** + * Adds a log record at the ALERT level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addAlert($message, array $context = array()) + { + return $this->addRecord(self::ALERT, $message, $context); + } + + /** + * Adds a log record at the EMERGENCY level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addEmergency($message, array $context = array()) + { + return $this->addRecord(self::EMERGENCY, $message, $context); + } + + /** + * Gets the name of the logging level. + * + * @param integer $level + * @return string + */ + public static function getLevelName($level) + { + return self::$levels[$level]; + } + + /** + * Checks whether the Logger has a handler that listens on the given level + * + * @param integer $level + * @return Boolean + */ + public function isHandling($level) + { + $record = array( + 'message' => '', + 'context' => array(), + 'level' => $level, + 'level_name' => self::getLevelName($level), + 'channel' => $this->name, + 'datetime' => new \DateTime(), + 'extra' => array(), + ); + + foreach ($this->handlers as $key => $handler) { + if ($handler->isHandling($record)) { + return true; + } + } + + return false; + } + + /** + * Adds a log record at the DEBUG level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function debug($message, array $context = array()) + { + return $this->addRecord(self::DEBUG, $message, $context); + } + + /** + * Adds a log record at the INFO level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function info($message, array $context = array()) + { + return $this->addRecord(self::INFO, $message, $context); + } + + /** + * Adds a log record at the INFO level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function notice($message, array $context = array()) + { + return $this->addRecord(self::NOTICE, $message, $context); + } + + /** + * Adds a log record at the WARNING level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function warn($message, array $context = array()) + { + return $this->addRecord(self::WARNING, $message, $context); + } + + /** + * Adds a log record at the ERROR level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function err($message, array $context = array()) + { + return $this->addRecord(self::ERROR, $message, $context); + } + + /** + * Adds a log record at the CRITICAL level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function crit($message, array $context = array()) + { + return $this->addRecord(self::CRITICAL, $message, $context); + } + + /** + * Adds a log record at the ALERT level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function alert($message, array $context = array()) + { + return $this->addRecord(self::ALERT, $message, $context); + } + + /** + * Adds a log record at the EMERGENCY level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function emerg($message, array $context = array()) + { + return $this->addRecord(self::EMERGENCY, $message, $context); + } +} diff --git a/Monolog/Processor/IntrospectionProcessor.php b/Monolog/Processor/IntrospectionProcessor.php new file mode 100755 index 0000000..b126218 --- /dev/null +++ b/Monolog/Processor/IntrospectionProcessor.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Injects line/file:class/function where the log message came from + * + * Warning: This only works if the handler processes the logs directly. + * If you put the processor on a handler that is behind a FingersCrossedHandler + * for example, the processor will only be called once the trigger level is reached, + * and all the log records will have the same file/line/.. data from the call that + * triggered the FingersCrossedHandler. + * + * @author Jordi Boggiano + */ +class IntrospectionProcessor +{ + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + $trace = debug_backtrace(); + + // skip first since it's always the current method + array_shift($trace); + // the call_user_func call is also skipped + array_shift($trace); + + $i = 0; + while (isset($trace[$i]['class']) && false !== strpos($trace[$i]['class'], 'Monolog\\')) { + $i++; + } + + // we should have the call source now + $record['extra'] = array_merge( + $record['extra'], + array( + 'file' => isset($trace[$i-1]['file']) ? $trace[$i-1]['file'] : null, + 'line' => isset($trace[$i-1]['line']) ? $trace[$i-1]['line'] : null, + 'class' => isset($trace[$i]['class']) ? $trace[$i]['class'] : null, + 'function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : null, + ) + ); + + return $record; + } +} diff --git a/Monolog/Processor/MemoryPeakUsageProcessor.php b/Monolog/Processor/MemoryPeakUsageProcessor.php new file mode 100755 index 0000000..e48672b --- /dev/null +++ b/Monolog/Processor/MemoryPeakUsageProcessor.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Injects memory_get_peak_usage in all records + * + * @see Monolog\Processor\MemoryProcessor::__construct() for options + * @author Rob Jensen + */ +class MemoryPeakUsageProcessor extends MemoryProcessor +{ + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + $bytes = memory_get_peak_usage($this->realUsage); + $formatted = self::formatBytes($bytes); + + $record['extra'] = array_merge( + $record['extra'], + array( + 'memory_peak_usage' => $formatted, + ) + ); + + return $record; + } +} diff --git a/Monolog/Processor/MemoryProcessor.php b/Monolog/Processor/MemoryProcessor.php new file mode 100755 index 0000000..7551043 --- /dev/null +++ b/Monolog/Processor/MemoryProcessor.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Some methods that are common for all memory processors + * + * @author Rob Jensen + */ +abstract class MemoryProcessor +{ + protected $realUsage; + + /** + * @param boolean $realUsage + */ + public function __construct($realUsage = true) + { + $this->realUsage = (boolean) $realUsage; + } + + /** + * Formats bytes into a human readable string + * + * @param int $bytes + * @return string + */ + protected static function formatBytes($bytes) + { + $bytes = (int) $bytes; + + if ($bytes > 1024*1024) { + return round($bytes/1024/1024, 2).' MB'; + } elseif ($bytes > 1024) { + return round($bytes/1024, 2).' KB'; + } + + return $bytes . ' B'; + } + +} diff --git a/Monolog/Processor/MemoryUsageProcessor.php b/Monolog/Processor/MemoryUsageProcessor.php new file mode 100755 index 0000000..2c4a807 --- /dev/null +++ b/Monolog/Processor/MemoryUsageProcessor.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Injects memory_get_usage in all records + * + * @see Monolog\Processor\MemoryProcessor::__construct() for options + * @author Rob Jensen + */ +class MemoryUsageProcessor extends MemoryProcessor +{ + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + $bytes = memory_get_usage($this->realUsage); + $formatted = self::formatBytes($bytes); + + $record['extra'] = array_merge( + $record['extra'], + array( + 'memory_usage' => $formatted, + ) + ); + + return $record; + } +} diff --git a/Monolog/Processor/WebProcessor.php b/Monolog/Processor/WebProcessor.php new file mode 100755 index 0000000..e297c23 --- /dev/null +++ b/Monolog/Processor/WebProcessor.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Processor; + +/** + * Injects url/method and remote IP of the current web request in all records + * + * @author Jordi Boggiano + */ +class WebProcessor +{ + protected $serverData; + + /** + * @param mixed $serverData array or object w/ ArrayAccess that provides access to the $_SERVER data + */ + public function __construct($serverData = null) + { + if (null === $serverData) { + $this->serverData =& $_SERVER; + } elseif (is_array($serverData) || $serverData instanceof \ArrayAccess) { + $this->serverData = $serverData; + } else { + throw new \UnexpectedValueException('$serverData must be an array or object implementing ArrayAccess.'); + } + } + + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + // skip processing if for some reason request data + // is not present (CLI or wonky SAPIs) + if (!isset($this->serverData['REQUEST_URI'])) { + return $record; + } + + if (!isset($this->serverData['HTTP_REFERER'])) { + $this->serverData['HTTP_REFERER'] = null; + } + + $record['extra'] = array_merge( + $record['extra'], + array( + 'url' => $this->serverData['REQUEST_URI'], + 'ip' => $this->serverData['REMOTE_ADDR'], + 'http_method' => $this->serverData['REQUEST_METHOD'], + 'server' => $this->serverData['SERVER_NAME'], + 'referrer' => $this->serverData['HTTP_REFERER'], + ) + ); + + return $record; + } +} diff --git a/Trackvia/Api.php b/Trackvia/Api.php new file mode 100644 index 0000000..d0e6835 --- /dev/null +++ b/Trackvia/Api.php @@ -0,0 +1,303 @@ +request = new Request(); + $this->auth = new Authentication($this->request, $params); + } + + /** + * Authenticate the user with OAuth2 + * @return array Access token data + */ + public function authenticate() + { + return $this->auth->authenticate(); + } + + /** + * Set the tokend ata for authentication + * @param [type] $tokenData [description] + */ + public function setTokenData($tokenData) + { + $this->auth->setTokenData($tokenData); + } + + /** + * Attach an event listener. + * @param string $event + * @param string|array $callback A callback function name that can be passed into call_user_func() + */ + public function on($event, $callback) + { + // if (!array_key_exists($event, $this->events)) { + // throw new Exception("Cannot bind to event \"$event\". This event is not supported."); + // } + // + if (!isset($this->events[$event])) { + // initialize an array for this event name + $this->events[$event] = array(); + } + + if (!is_callable($callback)) { + throw new Exception('Callback cannot is not callable. Check you have the right function name.'); + } + + $this->events[$event][] = $callback; + } + + /** + * Trigger a binded event. + * This will call all binded callback functions for a given event. + * @param string $event The event name + */ + public function trigger($event) + { + if (!array_key_exists($event, $this->events)) { + throw new Exception("Cannot trigger event \"$event\". This event is not supported."); + } + + // set different args based on event name + switch ($event) { + case 'new_token': + $data = array( + 'access_token' => $this->accessToken, + 'refresh_token' => $this->refresh, + 'expires_at' => $this->expiresAt + ); + break; + } + // loop through each callback for this event + foreach ($this->events[$event] as $callback) { + call_user_func($callback, $data); + } + } + + /** + * Check if the response failed and if the token is expired. + * If it is expired, use the refresh token to get a new one + * @param array $response + * @return boolean + */ + private function checkResponse($response) + { + if (is_array($response) && isset($response['error_description'])) { + if ($response['error_description'] == self::EXPIRED_ACCESS_TOKEN) { + + } + } + } + + private function api($url, $httpMethod = 'GET') + { + // check for access token + // if (!$this->auth->hasAccessToken() || $this->auth->isAccessTokenExpired()) { + + // } + $this->authenticate(); + + $accessToken = $this->auth->getAccessToken(); + + if (!$accessToken) { + // should have a token at this point + // if not, something went wrong + throw new Exception('Cannot make an api request without an access token'); + } + + // save this request in case we need to use the refresh token + $this->lastRequest = array( + 'url' => $url + // 'data' => $data + ); + + // make the request with current access token + $response = $this->request->request($url, $httpMethod, array( + 'access_token' => $accessToken + )); + + $this->trigger('api_request', array('url' => $url)); + + $this->checkResponse($response); + + if (!$response && $this->request->isTokenExpired()) { + //use refresh token to request new access token + if ($this->authenticate()) { + // redo initial api request + } + } + } + + /** + * Get a list of all apps, or a single app. + * + * Optional app_id parameter lets you specify one app if you want. + * Leave is empty to get all apps back. + * + * @param integer $appId + * @return array Array of app data returned from the api + */ + public function getApps($appId = null) + { + if ($appId != null && !is_int($appId)) { + throw new Exception('App ID must be an integer'); + } + + // build the url + $url = self::BASE_URL . self::APPS_URL . $appId; + + return $this->api($url, 'GET'); + } + + /** + * Get data for a single app by app_id. + * This will provide you with all the tables available for this app. + * + * @param integer $appId + * @return array Array of app data returned fromt the api + */ + public function getApp($appId) + { + $this->getApps($appId); + } + + /** + * Get table data back for a table_id. + * This will provide you all the views available for this table. + * + * @param integer $tableId + * @return array Array of table data returned from the api + */ + public function getTable($tableId) + { + if (!is_int($tableId)) { + throw new Exception('Table ID must be an integer'); + } + + // build the url + $url = self::BASE_URL . self::TABLES_URL . $tableId; + + return $this->api($url, 'GET'); + } + + /** + * Get view data back for a view_id. + * This will provide you with all the records under this view. + * + * @param integer $viewId + * @return array Array of view data returned from the api + */ + public function getView($viewId) + { + if (!is_int($viewId)) { + throw new Exception('View ID must be an integer'); + } + + // build the url + $url = self::BASE_URL . self::VIEWS_URL . $viewId; + + return $this->api($url); + } + + /** + * Get Record data back for a record_id. + * This will provide you with all the column data for a record. + * + * @param integer $recordId + * @return array Array of Record data returned from the api + */ + public function getRecord($recordId) + { + if (!is_int($recordId)) { + throw new Exception('Record ID must be an integer'); + } + + // build the url + $url = self::BASE_URL . self::RECORDS_URL . $recordId; + + return $this->api($url); + } + + public function addRecord($data) + { + # code... + } + + public function addRecords($data) + { + # code... + } + + public function updateRecord($id) + { + # code... + } + + public function updateRecords($data) + { + # code... + } + + public function deleteRecord($id) + { + # code... + } + + public function deleteRecords($data) + { + # code... + } + + public function search($tableId, $term) + { + # code... + } + +} \ No newline at end of file diff --git a/Trackvia/Authentication.php b/Trackvia/Authentication.php new file mode 100644 index 0000000..7c2e996 --- /dev/null +++ b/Trackvia/Authentication.php @@ -0,0 +1,285 @@ +request = $request; + + if (!is_array($params)) { + throw new Exception('You must pass in your client_id and client_secrect'); + } + if (!isset($params['client_id'])) { + throw new Exception('No client_id provided. This is required.'); + } + if (!isset($params['client_secret'])) { + throw new Exception('No client_secrect provided. This is required.'); + } + + $this->clientId = $params['client_id']; + $this->clientSecret = $params['client_secret']; + + if (isset($params['username'])) { + // user credentials flow is being used + $this->setUserCreds($params['username'], $params['password']); + } + } + + /** + * Set the user credentials to use for authentication + * @param [type] $username [description] + * @param [type] $password [description] + */ + public function setUserCreds($username, $password) + { + $this->userCreds = array( + 'username' => $username, + 'password' => $password + ); + } + + /** + * Whether or not user creds are provided + * @return boolean + */ + private function hasUserCreds() + { + return ( + !empty($this->userCreds) && + isset($this->userCreds['username']) && + isset($this->userCreds['password']) + ); + } + + /** + * Set the token data to use for authentication. + * @param array $params + */ + public function setTokenData($params) + { + $this->tokenData = $params; + } + + /** + * Get the currently set token data + * @return array + */ + public function getTokenData() + { + return $this->tokenData; + } + + /** + * Check if there is an access token set. + * @return boolean + */ + public function hasAccessToken() + { + return !empty($this->tokenData) && isset($this->tokenData['access_token']); + } + + /** + * Get the current access token. + * @return string + */ + public function getAccessToken() + { + return $this->tokenData['access_token']; + } + + /** + * Check if there is a refresh token set. + * @return boolean + */ + public function hasRefreshToken() + { + return !empty($this->tokenData) && isset($this->tokenData['refresh_token']); + } + + /** + * Get the current refresh token. + * @return string + */ + public function getRefreshToken() + { + return $this->tokenData['refresh_token']; + } + + /** + * Clear the current token data + */ + private function clearTokenData() + { + $this->tokenData = null; + } + + /** + * Check if the current token expired. + * We use the expired_at time that should be set by the client. + * @return boolean + */ + private function isAccessTokenExpired() + { + return $this->tokenData['expires_at'] > time(); + } + + /** + * Check if there is an access token and if it is expired base on the expired_at property. + * Not a valid token if either condition fails. + * + * @return boolean + */ + private function isAccessTokenValid() + { + return $this->hasAccessToken() && !$this->isAccessTokenExpired(); + } + + /** + * Get an access token from the Trackvia OAuth2 server. + * + * @param string $username The user's username credential + * @param string $password The user's password credential + * @return string The access token for the user + */ + public function requestTokenWithUserCreds($username, $password) + { + $url = $this->getTokenUrl(); + + $response = $this->request->post($url, array( + 'client_id' => $this->clientId, + 'client_secret' => $this->clientSecret, + 'grant_type' => 'password', + 'username' => $username, + 'password' => $password + )); + + if (!$response) { + throw new Exception('Requesting Access Token failed'); + } + + $this->tokenData = $response; + $this->tokenData['expires_at'] = $this->tokenData['expires_in'] + time(); + + return $this->tokenData; + } + + /** + * Get a new access token with a refresh token. + * + * @param string $refreshToken + * @return array Array of token data returned from the auth server + */ + public function requestTokenWithRefreshToken($refreshToken) + { + // use the refresh token to get a new access token + $url = $this->getTokenUrl(); + + $response = $this->request->post($url, array( + 'client_id' => $this->clientId, + 'client_secret' => $this->clientSecret, + 'grant_type' => 'refresh_token', + 'refresh_token' => $refreshToken + )); + + $this->tokenData = $response; + $this->tokenData['expires_at'] = $this->tokenData['expires_in'] + time(); + + return $this->tokenData; + } + + /** + * Authenticate the user based on what parameters have been set so far. + * If there is no token, request one with user creds if they exist. + * + * @return array Access token data + */ + public function authenticate() + { + $response = true; + + if (!$this->isAccessTokenValid()) { + if (!$this->hasRefreshToken()) { + // no tokens available, so we need to request new ones + + // check for user credentials flow first + if ($this->hasUserCreds()) { + $response = $this->requestTokenWithUserCreds( + $this->userCreds['username'], + $this->userCreds['password'] + ); + } + + //TODO add support for redirecting user to auth trackvia endpoint + + } + elseif ($this->hasRefreshToken()) { + // use the refresh token to get a new access token + $response = $this->requestTokenWithRefreshToken($this->getRefreshToken()); + + //TODO if the refresh token is expired we need to automatically request a new access token + } + } + + //TODO check for response errors here + + + return $response; + } + + private function getAuthUrl() + { + return TrackviaApi::BASE_URL . self::AUTH_URL; + } + + private function getTokenUrl() + { + return TrackviaApi::BASE_URL . self::TOKEN_URL; + } +} \ No newline at end of file diff --git a/Trackvia/Log.php b/Trackvia/Log.php new file mode 100644 index 0000000..b96719c --- /dev/null +++ b/Trackvia/Log.php @@ -0,0 +1,32 @@ +log = new Logger('name'); + $this->log->pushHandler(new StreamHandler('/Users/coake/api.log', Logger::WARNING)); + + $this->initListeners(); + } + + private function initListeners() + { + $subject->on('api_request', array($this, 'onApiRequest')); + } + + public function onApiRequest($data) + { + var_dump($data); + $this->log->addInfo("API Request made to url $url"); + } +} \ No newline at end of file diff --git a/Trackvia/Request.php b/Trackvia/Request.php new file mode 100644 index 0000000..c54a5dc --- /dev/null +++ b/Trackvia/Request.php @@ -0,0 +1,128 @@ +curl = curl_init(); + } + + public function setReturnType($type) + { + # code... + } + + public function request($url, $httpMethod = 'POST', $data = null) + { + if ( !in_array($httpMethod, array('POST', 'GET', 'PUT', 'DELETE')) ) { + throw new Exception('Request type "' . $httpMethod . '" not supported'); + } + $ch = $this->curl; + + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $httpMethod); + if (is_array($data) && count($data) > 0) { + // set any post data + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + } + + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); + + $response = curl_exec($ch); + $response = json_decode($response, true); + + if (isset($response['error'])) { + $this->parseError($reponse); + return false; + } + return $response; + } + + /** + * GET request. + * Additional data will be appended to the URL. + * + * @param string $url + * @param array $data Data to add on to the request + * @return array + */ + public function get($url, $data) + { + $url = $url . '?' . http_build_query($data); + return $this->request($url, 'GET'); + } + + /** + * POST request + * + * @param string $url + * @param aray $data POST fields to send with request + * @return array + */ + public function post($url, $data) + { + return $this->request($url, 'POST', $data); + } + + /** + * PUT request + * + * @param string $url + * @param aray $data Data fields to send with request + * @return array + */ + public function put($url, $data) + { + return $this->request($url, 'PUT', $data); + } + + /** + * DELETE request + * + * @param string $url + * @param aray $data Data fields to send with request + * @return array + */ + public function delete($url, $data) + { + return $this->request($url, 'DELETE', $data); + } + + private function parseError($data) + { + switch ($data['error_description']) { + case self::ERROR_EXPIRED_TOKEN: + $this->isTokenExpired = true; + // return here so we don't throw this error + // so we can use the refresh token + return; + } + + // throw an exception with the returned error message + throw new Exception($data['error_description']); + } + + /** + * Repeat the last + * @return [type] [description] + */ + public function repeatRequest() + { + + } + + public function isTokenExpired() + { + return $this->isTokenExpired; + } +} \ No newline at end of file diff --git a/api_sample.php b/api_sample.php new file mode 100644 index 0000000..52aa57d --- /dev/null +++ b/api_sample.php @@ -0,0 +1,93 @@ + 'ZDA4ZTAzZWNhODI1YzViNGU0YjUwOWU2ZWUxMTljYzUxYzc0YTExMjViNTYyYWYwMDcxYTliZGM1ZTBiMjJhMANULL', + 'refresh_token' => '', + 'expires_at' => '' + ); +} + +$tokenData = load_saved_token_data(); +$extraParams = array('this can be whatever you want'); + +// Create a TrackviaApi object with your clientId and secret. +// The client_id and secret are only user when you need to request a new access token. +$tv = new Api(array( + 'client_id' => $clientId, + 'client_secret' => $clientSecret, + 'username' => $username, + 'password' => $password +)); + +// setup the logger for debugging +$log = new Log($tv); + +// attach a listener function for when a new token is generated so you can save it to a database +$tv->on('new_token', 'save_token_data', $extraParams); + +// If there is saved token data to use, set it now +if (!empty($savedToken) && isset($savedToken['access_token'])) { + $tv->setTokenData(array( + 'access_token' => $savedToken['access_token'], + 'refresh_token' => $savedToken['refresh_token'], + 'expires_at' => $savedToken['expires_at'] + )); +} + +if (!$tokenData) { + // request a new access token with user credentials you already passed in + // this will trigger the "new_token" listener you created above so you can save the token data + $tv->authenticate(); + +} else { + // set the current token data you have + // the library will auto check the expiresAt so we don't waste a request + // If it is expired, a new access token + $tv->setTokenData(array( + 'access_token' => $tokenData['access_token'], + 'refresh_token' => $tokenData['refresh_token'], + 'expires_at' => $tokenData['expires_at'] + )); +} + + +$appId = 1; + +if ($apps = $tv->getApps()) { + // do something with apps list + // var_dump($apps); exit; +} else { + // request failed + // did access token expire? + +} + + +var_dump($apps); exit; \ No newline at end of file From 2ffc9b5d7e32d20430c44caf61a0ba17a6dbc40d Mon Sep 17 00:00:00 2001 From: Chris Oake Date: Mon, 30 Jul 2012 18:01:03 -0600 Subject: [PATCH 02/14] Made EventDispatcher abstract class. Added some logging --- Trackvia/EventDispatcher.php | 53 ++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Trackvia/EventDispatcher.php diff --git a/Trackvia/EventDispatcher.php b/Trackvia/EventDispatcher.php new file mode 100644 index 0000000..5d9f10d --- /dev/null +++ b/Trackvia/EventDispatcher.php @@ -0,0 +1,53 @@ + + */ +abstract class EventDispatcher +{ + /** + * Array to store callbacks for different event names + * @var array + */ + protected $events = array(); + + /** + * Attach an event listener. + * @param string $event + * @param string|array $callback A callback function name that can be passed into call_user_func() + */ + public function on($event, $callback) + { + if (!isset($this->events[$event])) { + // initialize an array for this event name + $this->events[$event] = array(); + } + + if (!is_callable($callback)) { + throw new \Exception("Callback argument is not callable. Check you have the right function name."); + } + + $this->events[$event][] = $callback; + } + + /** + * Trigger a binded event. + * This will call all binded callback functions for a given event. + * @param string $event The event name + * @param array Array of optional data to trigger with the event + */ + public function trigger($event, $data = array()) + { + if (isset($this->events[$event])) { + // loop through each callback for this event + foreach ($this->events[$event] as $callback) { + call_user_func($callback, $data); + } + } + } +} \ No newline at end of file From 11f5ee3e0ca194bf5020951ea71ad705c1f2680c Mon Sep 17 00:00:00 2001 From: Chris Oake Date: Mon, 6 Aug 2012 18:03:01 -0600 Subject: [PATCH 03/14] Api working with all actions supported --- Monolog/Formatter/ChromePHPFormatter.php | 79 --- Monolog/Formatter/FormatterInterface.php | 36 -- Monolog/Formatter/GelfMessageFormatter.php | 94 ---- Monolog/Formatter/JsonFormatter.php | 38 -- Monolog/Formatter/LineFormatter.php | 90 ---- Monolog/Formatter/NormalizerFormatter.php | 92 ---- Monolog/Formatter/WildfireFormatter.php | 99 ---- Monolog/Handler/AbstractHandler.php | 174 ------- Monolog/Handler/AbstractProcessingHandler.php | 66 --- Monolog/Handler/AmqpHandler.php | 69 --- Monolog/Handler/BufferHandler.php | 73 --- Monolog/Handler/ChromePHPHandler.php | 126 ----- .../ActivationStrategyInterface.php | 28 -- .../ErrorLevelActivationStrategy.php | 32 -- Monolog/Handler/FingersCrossedHandler.php | 104 ---- Monolog/Handler/FirePHPHandler.php | 160 ------ Monolog/Handler/GelfHandler.php | 66 --- Monolog/Handler/GroupHandler.php | 74 --- Monolog/Handler/HandlerInterface.php | 77 --- Monolog/Handler/MailHandler.php | 55 --- Monolog/Handler/MongoDBHandler.php | 51 -- Monolog/Handler/NativeMailerHandler.php | 65 --- Monolog/Handler/NullHandler.php | 45 -- Monolog/Handler/RotatingFileHandler.php | 109 ---- Monolog/Handler/SocketHandler.php | 272 ---------- Monolog/Handler/StreamHandler.php | 71 --- Monolog/Handler/SwiftMailerHandler.php | 55 --- Monolog/Handler/SyslogHandler.php | 110 ----- Monolog/Handler/TestHandler.php | 140 ------ Monolog/Logger.php | 466 ------------------ Monolog/Processor/IntrospectionProcessor.php | 58 --- .../Processor/MemoryPeakUsageProcessor.php | 40 -- Monolog/Processor/MemoryProcessor.php | 50 -- Monolog/Processor/MemoryUsageProcessor.php | 40 -- Monolog/Processor/WebProcessor.php | 66 --- Trackvia/Api.php | 327 +++++++----- Trackvia/Authentication.php | 193 ++++++-- Trackvia/EventDispatcher.php | 19 +- Trackvia/Log.php | 130 ++++- Trackvia/Request.php | 231 +++++---- api_sample.php | 93 ---- example.php | 210 ++++++++ 42 files changed, 790 insertions(+), 3683 deletions(-) delete mode 100755 Monolog/Formatter/ChromePHPFormatter.php delete mode 100755 Monolog/Formatter/FormatterInterface.php delete mode 100755 Monolog/Formatter/GelfMessageFormatter.php delete mode 100755 Monolog/Formatter/JsonFormatter.php delete mode 100755 Monolog/Formatter/LineFormatter.php delete mode 100755 Monolog/Formatter/NormalizerFormatter.php delete mode 100755 Monolog/Formatter/WildfireFormatter.php delete mode 100755 Monolog/Handler/AbstractHandler.php delete mode 100755 Monolog/Handler/AbstractProcessingHandler.php delete mode 100755 Monolog/Handler/AmqpHandler.php delete mode 100755 Monolog/Handler/BufferHandler.php delete mode 100755 Monolog/Handler/ChromePHPHandler.php delete mode 100755 Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php delete mode 100755 Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php delete mode 100755 Monolog/Handler/FingersCrossedHandler.php delete mode 100755 Monolog/Handler/FirePHPHandler.php delete mode 100755 Monolog/Handler/GelfHandler.php delete mode 100755 Monolog/Handler/GroupHandler.php delete mode 100755 Monolog/Handler/HandlerInterface.php delete mode 100755 Monolog/Handler/MailHandler.php delete mode 100755 Monolog/Handler/MongoDBHandler.php delete mode 100755 Monolog/Handler/NativeMailerHandler.php delete mode 100755 Monolog/Handler/NullHandler.php delete mode 100755 Monolog/Handler/RotatingFileHandler.php delete mode 100755 Monolog/Handler/SocketHandler.php delete mode 100755 Monolog/Handler/StreamHandler.php delete mode 100755 Monolog/Handler/SwiftMailerHandler.php delete mode 100755 Monolog/Handler/SyslogHandler.php delete mode 100755 Monolog/Handler/TestHandler.php delete mode 100755 Monolog/Logger.php delete mode 100755 Monolog/Processor/IntrospectionProcessor.php delete mode 100755 Monolog/Processor/MemoryPeakUsageProcessor.php delete mode 100755 Monolog/Processor/MemoryProcessor.php delete mode 100755 Monolog/Processor/MemoryUsageProcessor.php delete mode 100755 Monolog/Processor/WebProcessor.php mode change 100644 => 100755 Trackvia/Api.php mode change 100644 => 100755 Trackvia/Authentication.php mode change 100644 => 100755 Trackvia/Log.php mode change 100644 => 100755 Trackvia/Request.php delete mode 100644 api_sample.php create mode 100755 example.php diff --git a/Monolog/Formatter/ChromePHPFormatter.php b/Monolog/Formatter/ChromePHPFormatter.php deleted file mode 100755 index 56d3e27..0000000 --- a/Monolog/Formatter/ChromePHPFormatter.php +++ /dev/null @@ -1,79 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Formatter; - -use Monolog\Logger; - -/** - * Formats a log message according to the ChromePHP array format - * - * @author Christophe Coevoet - */ -class ChromePHPFormatter implements FormatterInterface -{ - /** - * Translates Monolog log levels to Wildfire levels. - */ - private $logLevels = array( - Logger::DEBUG => 'log', - Logger::INFO => 'info', - Logger::NOTICE => 'info', - Logger::WARNING => 'warn', - Logger::ERROR => 'error', - Logger::CRITICAL => 'error', - Logger::ALERT => 'error', - Logger::EMERGENCY => 'error', - ); - - /** - * {@inheritdoc} - */ - public function format(array $record) - { - // Retrieve the line and file if set and remove them from the formatted extra - $backtrace = 'unknown'; - if (isset($record['extra']['file']) && isset($record['extra']['line'])) { - $backtrace = $record['extra']['file'].' : '.$record['extra']['line']; - unset($record['extra']['file']); - unset($record['extra']['line']); - } - - $message = array('message' => $record['message']); - if ($record['context']) { - $message['context'] = $record['context']; - } - if ($record['extra']) { - $message['extra'] = $record['extra']; - } - if (count($message) === 1) { - $message = reset($message); - } - - return array( - $record['channel'], - $message, - $backtrace, - $this->logLevels[$record['level']], - ); - } - - public function formatBatch(array $records) - { - $formatted = array(); - - foreach ($records as $record) { - $formatted[] = $this->format($record); - } - - return $formatted; - } -} diff --git a/Monolog/Formatter/FormatterInterface.php b/Monolog/Formatter/FormatterInterface.php deleted file mode 100755 index b5de751..0000000 --- a/Monolog/Formatter/FormatterInterface.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Formatter; - -/** - * Interface for formatters - * - * @author Jordi Boggiano - */ -interface FormatterInterface -{ - /** - * Formats a log record. - * - * @param array $record A record to format - * @return mixed The formatted record - */ - public function format(array $record); - - /** - * Formats a set of log records. - * - * @param array $records A set of records to format - * @return mixed The formatted set of records - */ - public function formatBatch(array $records); -} diff --git a/Monolog/Formatter/GelfMessageFormatter.php b/Monolog/Formatter/GelfMessageFormatter.php deleted file mode 100755 index 0856f86..0000000 --- a/Monolog/Formatter/GelfMessageFormatter.php +++ /dev/null @@ -1,94 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Formatter; - -use Monolog\Logger; -use Gelf\Message; - -/** - * Serializes a log message to GELF - * @see http://www.graylog2.org/about/gelf - * - * @author Matt Lehner - */ -class GelfMessageFormatter extends NormalizerFormatter -{ - /** - * @var string the name of the system for the Gelf log message - */ - protected $systemName; - - /** - * @var string a prefix for 'extra' fields from the Monolog record (optional) - */ - protected $extraPrefix; - - /** - * @var string a prefix for 'context' fields from the Monolog record (optional) - */ - protected $contextPrefix; - - /** - * Translates Monolog log levels to Graylog2 log priorities. - */ - private $logLevels = array( - Logger::DEBUG => LOG_DEBUG, - Logger::INFO => LOG_INFO, - Logger::NOTICE => LOG_NOTICE, - Logger::WARNING => LOG_WARNING, - Logger::ERROR => LOG_ERR, - Logger::CRITICAL => LOG_CRIT, - Logger::ALERT => LOG_ALERT, - Logger::EMERGENCY => LOG_EMERG, - ); - - public function __construct($systemName = null, $extraPrefix = null, $contextPrefix = 'ctxt_') - { - parent::__construct('U.u'); - - $this->systemName = $systemName ?: gethostname(); - - $this->extraPrefix = $extraPrefix; - $this->contextPrefix = $contextPrefix; - } - - /** - * {@inheritdoc} - */ - public function format(array $record) - { - $record = parent::format($record); - $message = new Message(); - $message - ->setTimestamp($record['datetime']) - ->setShortMessage((string) $record['message']) - ->setFacility($record['channel']) - ->setHost($this->systemName) - ->setLine(isset($record['extra']['line']) ? $record['extra']['line'] : null) - ->setFile(isset($record['extra']['file']) ? $record['extra']['file'] : null) - ->setLevel($this->logLevels[$record['level']]); - - // Do not duplicate these values in the additional fields - unset($record['extra']['line']); - unset($record['extra']['file']); - - foreach ($record['extra'] as $key => $val) { - $message->setAdditional($this->extraPrefix . $key, is_scalar($val) ? $val : $this->toJson($val)); - } - - foreach ($record['context'] as $key => $val) { - $message->setAdditional($this->contextPrefix . $key, is_scalar($val) ? $val : $this->toJson($val)); - } - - return $message; - } -} diff --git a/Monolog/Formatter/JsonFormatter.php b/Monolog/Formatter/JsonFormatter.php deleted file mode 100755 index 822af0e..0000000 --- a/Monolog/Formatter/JsonFormatter.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Formatter; - -/** - * Encodes whatever record data is passed to it as json - * - * This can be useful to log to databases or remote APIs - * - * @author Jordi Boggiano - */ -class JsonFormatter implements FormatterInterface -{ - /** - * {@inheritdoc} - */ - public function format(array $record) - { - return json_encode($record); - } - - /** - * {@inheritdoc} - */ - public function formatBatch(array $records) - { - return json_encode($records); - } -} diff --git a/Monolog/Formatter/LineFormatter.php b/Monolog/Formatter/LineFormatter.php deleted file mode 100755 index 1054dbb..0000000 --- a/Monolog/Formatter/LineFormatter.php +++ /dev/null @@ -1,90 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Formatter; - -/** - * Formats incoming records into a one-line string - * - * This is especially useful for logging to files - * - * @author Jordi Boggiano - * @author Christophe Coevoet - */ -class LineFormatter extends NormalizerFormatter -{ - const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"; - - protected $format; - - /** - * @param string $format The format of the message - * @param string $dateFormat The format of the timestamp: one supported by DateTime::format - */ - public function __construct($format = null, $dateFormat = null) - { - $this->format = $format ?: static::SIMPLE_FORMAT; - parent::__construct($dateFormat); - } - - /** - * {@inheritdoc} - */ - public function format(array $record) - { - $vars = parent::format($record); - - $output = $this->format; - foreach ($vars['extra'] as $var => $val) { - if (false !== strpos($output, '%extra.'.$var.'%')) { - $output = str_replace('%extra.'.$var.'%', $this->convertToString($val), $output); - unset($vars['extra'][$var]); - } - } - foreach ($vars as $var => $val) { - $output = str_replace('%'.$var.'%', $this->convertToString($val), $output); - } - - return $output; - } - - public function formatBatch(array $records) - { - $message = ''; - foreach ($records as $record) { - $message .= $this->format($record); - } - - return $message; - } - - protected function normalize($data) - { - if (is_bool($data) || is_null($data)) { - return var_export($data, true); - } - - return parent::normalize($data); - } - - protected function convertToString($data) - { - if (null === $data || is_scalar($data)) { - return (string) $data; - } - - if (version_compare(PHP_VERSION, '5.4.0', '>=')) { - return json_encode($this->normalize($data), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); - } - - return stripslashes(json_encode($this->normalize($data))); - } -} diff --git a/Monolog/Formatter/NormalizerFormatter.php b/Monolog/Formatter/NormalizerFormatter.php deleted file mode 100755 index 6ce4a2e..0000000 --- a/Monolog/Formatter/NormalizerFormatter.php +++ /dev/null @@ -1,92 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Formatter; - -/** - * Normalizes incoming records to remove objects/resources so it's easier to dump to various targets - * - * @author Jordi Boggiano - */ -class NormalizerFormatter implements FormatterInterface -{ - const SIMPLE_DATE = "Y-m-d H:i:s"; - - protected $dateFormat; - - /** - * @param string $dateFormat The format of the timestamp: one supported by DateTime::format - */ - public function __construct($dateFormat = null) - { - $this->dateFormat = $dateFormat ?: static::SIMPLE_DATE; - } - - /** - * {@inheritdoc} - */ - public function format(array $record) - { - return $this->normalize($record); - } - - /** - * {@inheritdoc} - */ - public function formatBatch(array $records) - { - foreach ($records as $key => $record) { - $records[$key] = $this->format($record); - } - - return $records; - } - - protected function normalize($data) - { - if (null === $data || is_scalar($data)) { - return $data; - } - - if (is_array($data) || $data instanceof \Traversable) { - $normalized = array(); - - foreach ($data as $key => $value) { - $normalized[$key] = $this->normalize($value); - } - - return $normalized; - } - - if ($data instanceof \DateTime) { - return $data->format($this->dateFormat); - } - - if (is_object($data)) { - return sprintf("[object] (%s: %s)", get_class($data), $this->toJson($data)); - } - - if (is_resource($data)) { - return '[resource]'; - } - - return '[unknown('.gettype($data).')]'; - } - - protected function toJson($data) - { - if (version_compare(PHP_VERSION, '5.4.0', '>=')) { - return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); - } - - return json_encode($data); - } -} diff --git a/Monolog/Formatter/WildfireFormatter.php b/Monolog/Formatter/WildfireFormatter.php deleted file mode 100755 index 8ca6f2b..0000000 --- a/Monolog/Formatter/WildfireFormatter.php +++ /dev/null @@ -1,99 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Formatter; - -use Monolog\Logger; - -/** - * Serializes a log message according to Wildfire's header requirements - * - * @author Eric Clemmons (@ericclemmons) - * @author Christophe Coevoet - * @author Kirill chEbba Chebunin - */ -class WildfireFormatter extends NormalizerFormatter -{ - /** - * Translates Monolog log levels to Wildfire levels. - */ - private $logLevels = array( - Logger::DEBUG => 'LOG', - Logger::INFO => 'INFO', - Logger::NOTICE => 'INFO', - Logger::WARNING => 'WARN', - Logger::ERROR => 'ERROR', - Logger::CRITICAL => 'ERROR', - Logger::ALERT => 'ERROR', - Logger::EMERGENCY => 'ERROR', - ); - - /** - * {@inheritdoc} - */ - public function format(array $record) - { - // Retrieve the line and file if set and remove them from the formatted extra - $file = $line = ''; - if (isset($record['extra']['file'])) { - $file = $record['extra']['file']; - unset($record['extra']['file']); - } - if (isset($record['extra']['line'])) { - $line = $record['extra']['line']; - unset($record['extra']['line']); - } - - $record = $this->normalize($record); - $message = array('message' => $record['message']); - if ($record['context']) { - $message['context'] = $record['context']; - } - if ($record['extra']) { - $message['extra'] = $record['extra']; - } - if (count($message) === 1) { - $message = reset($message); - } - - // Create JSON object describing the appearance of the message in the console - $json = json_encode(array( - array( - 'Type' => $this->logLevels[$record['level']], - 'File' => $file, - 'Line' => $line, - 'Label' => $record['channel'], - ), - $message, - )); - - // The message itself is a serialization of the above JSON object + it's length - return sprintf( - '%s|%s|', - strlen($json), - $json - ); - } - - public function formatBatch(array $records) - { - throw new \BadMethodCallException('Batch formatting does not make sense for the WildfireFormatter'); - } - - protected function normalize($data) - { - if (is_object($data) && !$data instanceof \DateTime) { - return $data; - } - - return parent::normalize($data); - } -} diff --git a/Monolog/Handler/AbstractHandler.php b/Monolog/Handler/AbstractHandler.php deleted file mode 100755 index 2ea9f55..0000000 --- a/Monolog/Handler/AbstractHandler.php +++ /dev/null @@ -1,174 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Handler; - -use Monolog\Logger; -use Monolog\Formatter\FormatterInterface; -use Monolog\Formatter\LineFormatter; - -/** - * Base Handler class providing the Handler structure - * - * @author Jordi Boggiano - */ -abstract class AbstractHandler implements HandlerInterface -{ - protected $level = Logger::DEBUG; - protected $bubble = false; - - /** - * @var FormatterInterface - */ - protected $formatter; - protected $processors = array(); - - /** - * @param integer $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not - */ - public function __construct($level = Logger::DEBUG, $bubble = true) - { - $this->level = $level; - $this->bubble = $bubble; - } - - /** - * {@inheritdoc} - */ - public function isHandling(array $record) - { - return $record['level'] >= $this->level; - } - - /** - * {@inheritdoc} - */ - public function handleBatch(array $records) - { - foreach ($records as $record) { - $this->handle($record); - } - } - - /** - * Closes the handler. - * - * This will be called automatically when the object is destroyed - */ - public function close() - { - } - - /** - * {@inheritdoc} - */ - public function pushProcessor($callback) - { - if (!is_callable($callback)) { - throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), '.var_export($callback, true).' given'); - } - array_unshift($this->processors, $callback); - } - - /** - * {@inheritdoc} - */ - public function popProcessor() - { - if (!$this->processors) { - throw new \LogicException('You tried to pop from an empty processor stack.'); - } - - return array_shift($this->processors); - } - - /** - * {@inheritdoc} - */ - public function setFormatter(FormatterInterface $formatter) - { - $this->formatter = $formatter; - } - - /** - * {@inheritdoc} - */ - public function getFormatter() - { - if (!$this->formatter) { - $this->formatter = $this->getDefaultFormatter(); - } - - return $this->formatter; - } - - /** - * Sets minimum logging level at which this handler will be triggered. - * - * @param integer $level - */ - public function setLevel($level) - { - $this->level = $level; - } - - /** - * Gets minimum logging level at which this handler will be triggered. - * - * @return integer - */ - public function getLevel() - { - return $this->level; - } - - /** - * Sets the bubbling behavior. - * - * @param Boolean $bubble True means that bubbling is not permitted. - * False means that this handler allows bubbling. - */ - public function setBubble($bubble) - { - $this->bubble = $bubble; - } - - /** - * Gets the bubbling behavior. - * - * @return Boolean True means that bubbling is not permitted. - * False means that this handler allows bubbling. - */ - public function getBubble() - { - return $this->bubble; - } - - public function __destruct() - { - try { - $this->close(); - } catch (\Exception $e) { - // do nothing - } - } - - /** - * Gets the default formatter. - * - * @return FormatterInterface - */ - protected function getDefaultFormatter() - { - return new LineFormatter(); - } -} diff --git a/Monolog/Handler/AbstractProcessingHandler.php b/Monolog/Handler/AbstractProcessingHandler.php deleted file mode 100755 index e1e5b89..0000000 --- a/Monolog/Handler/AbstractProcessingHandler.php +++ /dev/null @@ -1,66 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Handler; - -/** - * Base Handler class providing the Handler structure - * - * Classes extending it should (in most cases) only implement write($record) - * - * @author Jordi Boggiano - * @author Christophe Coevoet - */ -abstract class AbstractProcessingHandler extends AbstractHandler -{ - /** - * {@inheritdoc} - */ - public function handle(array $record) - { - if ($record['level'] < $this->level) { - return false; - } - - $record = $this->processRecord($record); - - $record['formatted'] = $this->getFormatter()->format($record); - - $this->write($record); - - return false === $this->bubble; - } - - /** - * Writes the record down to the log of the implementing handler - * - * @param array $record - * @return void - */ - abstract protected function write(array $record); - - /** - * Processes a record. - * - * @param array $record - * @return array - */ - protected function processRecord(array $record) - { - if ($this->processors) { - foreach ($this->processors as $processor) { - $record = call_user_func($processor, $record); - } - } - - return $record; - } -} diff --git a/Monolog/Handler/AmqpHandler.php b/Monolog/Handler/AmqpHandler.php deleted file mode 100755 index 0070343..0000000 --- a/Monolog/Handler/AmqpHandler.php +++ /dev/null @@ -1,69 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Handler; - -use Monolog\Logger; -use Monolog\Formatter\JsonFormatter; - -class AmqpHandler extends AbstractProcessingHandler -{ - /** - * @var \AMQPExchange $exchange - */ - protected $exchange; - - /** - * @param \AMQPExchange $exchange AMQP exchange, ready for use - * @param string $exchangeName - * @param int $level - * @param bool $bubble Whether the messages that are handled can bubble up the stack or not - */ - public function __construct(\AMQPExchange $exchange, $exchangeName = 'log', $level = Logger::DEBUG, $bubble = true) - { - $this->exchange = $exchange; - $this->exchange->setName($exchangeName); - - parent::__construct($level, $bubble); - } - - /** - * {@inheritDoc} - */ - protected function write(array $record) - { - $data = $record["formatted"]; - - $routingKey = sprintf( - '%s.%s', - substr($record['level_name'], 0, 4), - $record['channel'] - ); - - $this->exchange->publish( - $data, - strtolower($routingKey), - 0, - array( - 'delivery_mode' => 2, - 'Content-type' => 'application/json' - ) - ); - } - - /** - * {@inheritDoc} - */ - protected function getDefaultFormatter() - { - return new JsonFormatter(); - } -} diff --git a/Monolog/Handler/BufferHandler.php b/Monolog/Handler/BufferHandler.php deleted file mode 100755 index afbb4a6..0000000 --- a/Monolog/Handler/BufferHandler.php +++ /dev/null @@ -1,73 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Handler; - -use Monolog\Logger; - -/** - * Buffers all records until closing the handler and then pass them as batch. - * - * This is useful for a MailHandler to send only one mail per request instead of - * sending one per log message. - * - * @author Christophe Coevoet - */ -class BufferHandler extends AbstractHandler -{ - protected $handler; - protected $bufferSize; - protected $buffer = array(); - - /** - * @param HandlerInterface $handler Handler. - * @param integer $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. - * @param integer $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not - */ - public function __construct(HandlerInterface $handler, $bufferSize = 0, $level = Logger::DEBUG, $bubble = true) - { - parent::__construct($level, $bubble); - $this->handler = $handler; - $this->bufferSize = $bufferSize; - - // __destructor() doesn't get called on Fatal errors - register_shutdown_function(array($this, 'close')); - } - - /** - * {@inheritdoc} - */ - public function handle(array $record) - { - if ($record['level'] < $this->level) { - return false; - } - - $this->buffer[] = $record; - if ($this->bufferSize > 0 && count($this->buffer) > $this->bufferSize) { - array_shift($this->buffer); - } - - return false === $this->bubble; - } - - /** - * {@inheritdoc} - */ - public function close() - { - if ($this->buffer) { - $this->handler->handleBatch($this->buffer); - $this->buffer = array(); - } - } -} diff --git a/Monolog/Handler/ChromePHPHandler.php b/Monolog/Handler/ChromePHPHandler.php deleted file mode 100755 index 86d7feb..0000000 --- a/Monolog/Handler/ChromePHPHandler.php +++ /dev/null @@ -1,126 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Handler; - -use Monolog\Formatter\ChromePHPFormatter; - -/** - * Handler sending logs to the ChromePHP extension (http://www.chromephp.com/) - * - * @author Christophe Coevoet - */ -class ChromePHPHandler extends AbstractProcessingHandler -{ - /** - * Version of the extension - */ - const VERSION = '3.0'; - - /** - * Header name - */ - const HEADER_NAME = 'X-ChromePhp-Data'; - - protected static $initialized = false; - - protected static $json = array( - 'version' => self::VERSION, - 'columns' => array('label', 'log', 'backtrace', 'type'), - 'rows' => array(), - ); - - protected $sendHeaders = true; - - /** - * {@inheritdoc} - */ - public function handleBatch(array $records) - { - $messages = array(); - - foreach ($records as $record) { - if ($record['level'] < $this->level) { - continue; - } - $messages[] = $this->processRecord($record); - } - - if (!empty($messages)) { - $messages = $this->getFormatter()->formatBatch($messages); - self::$json['rows'] = array_merge(self::$json['rows'], $messages); - $this->send(); - } - } - - /** - * {@inheritDoc} - */ - protected function getDefaultFormatter() - { - return new ChromePHPFormatter(); - } - - /** - * Creates & sends header for a record - * - * @see sendHeader() - * @see send() - * @param array $record - */ - protected function write(array $record) - { - self::$json['rows'][] = $record['formatted']; - - $this->send(); - } - - /** - * Sends the log header - * - * @see sendHeader() - */ - protected function send() - { - if (!self::$initialized) { - $this->sendHeaders = $this->headersAccepted(); - self::$json['request_uri'] = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; - - self::$initialized = true; - } - - $this->sendHeader(self::HEADER_NAME, base64_encode(utf8_encode(json_encode(self::$json)))); - } - - /** - * Send header string to the client - * - * @param string $header - * @param string $content - */ - protected function sendHeader($header, $content) - { - if (!headers_sent() && $this->sendHeaders) { - header(sprintf('%s: %s', $header, $content)); - } - } - - /** - * Verifies if the headers are accepted by the current user agent - * - * @return Boolean - */ - protected function headersAccepted() - { - return !isset($_SERVER['HTTP_USER_AGENT']) - || preg_match('{\bChrome/\d+[\.\d+]*\b}', $_SERVER['HTTP_USER_AGENT']); - } -} diff --git a/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php b/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php deleted file mode 100755 index c3e42ef..0000000 --- a/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Handler\FingersCrossed; - -/** - * Interface for activation strategies for the FingersCrossedHandler. - * - * @author Johannes M. Schmitt - */ -interface ActivationStrategyInterface -{ - /** - * Returns whether the given record activates the handler. - * - * @param array $record - * @return Boolean - */ - public function isHandlerActivated(array $record); -} diff --git a/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php b/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php deleted file mode 100755 index 7cd8ef1..0000000 --- a/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php +++ /dev/null @@ -1,32 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Handler\FingersCrossed; - -/** - * Error level based activation strategy. - * - * @author Johannes M. Schmitt - */ -class ErrorLevelActivationStrategy implements ActivationStrategyInterface -{ - private $actionLevel; - - public function __construct($actionLevel) - { - $this->actionLevel = $actionLevel; - } - - public function isHandlerActivated(array $record) - { - return $record['level'] >= $this->actionLevel; - } -} diff --git a/Monolog/Handler/FingersCrossedHandler.php b/Monolog/Handler/FingersCrossedHandler.php deleted file mode 100755 index 561ee7c..0000000 --- a/Monolog/Handler/FingersCrossedHandler.php +++ /dev/null @@ -1,104 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Handler; - -use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; -use Monolog\Handler\FingersCrossed\ActivationStrategyInterface; -use Monolog\Logger; - -/** - * Buffers all records until a certain level is reached - * - * The advantage of this approach is that you don't get any clutter in your log files. - * Only requests which actually trigger an error (or whatever your actionLevel is) will be - * in the logs, but they will contain all records, not only those above the level threshold. - * - * @author Jordi Boggiano - */ -class FingersCrossedHandler extends AbstractHandler -{ - protected $handler; - protected $activationStrategy; - protected $buffering = true; - protected $bufferSize; - protected $buffer = array(); - protected $stopBuffering; - - /** - * @param callback|HandlerInterface $handler Handler or factory callback($record, $fingersCrossedHandler). - * @param int|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action - * @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not - * @param Boolean $stopBuffering Whether the handler should stop buffering after being triggered (default true) - */ - public function __construct($handler, $activationStrategy = null, $bufferSize = 0, $bubble = true, $stopBuffering = true) - { - if (null === $activationStrategy) { - $activationStrategy = new ErrorLevelActivationStrategy(Logger::WARNING); - } - if (!$activationStrategy instanceof ActivationStrategyInterface) { - $activationStrategy = new ErrorLevelActivationStrategy($activationStrategy); - } - - $this->handler = $handler; - $this->activationStrategy = $activationStrategy; - $this->bufferSize = $bufferSize; - $this->bubble = $bubble; - $this->stopBuffering = $stopBuffering; - } - - /** - * {@inheritdoc} - */ - public function isHandling(array $record) - { - return true; - } - - /** - * {@inheritdoc} - */ - public function handle(array $record) - { - if ($this->buffering) { - $this->buffer[] = $record; - if ($this->bufferSize > 0 && count($this->buffer) > $this->bufferSize) { - array_shift($this->buffer); - } - if ($this->activationStrategy->isHandlerActivated($record)) { - if ($this->stopBuffering) { - $this->buffering = false; - } - if (!$this->handler instanceof HandlerInterface) { - $this->handler = call_user_func($this->handler, $record, $this); - } - if (!$this->handler instanceof HandlerInterface) { - throw new \RuntimeException("The factory callback should return a HandlerInterface"); - } - $this->handler->handleBatch($this->buffer); - $this->buffer = array(); - } - } else { - $this->handler->handle($record); - } - - return false === $this->bubble; - } - - /** - * Resets the state of the handler. Stops forwarding records to the wrapped handler. - */ - public function reset() - { - $this->buffering = true; - } -} diff --git a/Monolog/Handler/FirePHPHandler.php b/Monolog/Handler/FirePHPHandler.php deleted file mode 100755 index 0909218..0000000 --- a/Monolog/Handler/FirePHPHandler.php +++ /dev/null @@ -1,160 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Handler; - -use Monolog\Formatter\WildfireFormatter; - -/** - * Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol. - * - * @author Eric Clemmons (@ericclemmons) - */ -class FirePHPHandler extends AbstractProcessingHandler -{ - /** - * WildFire JSON header message format - */ - const PROTOCOL_URI = 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2'; - - /** - * FirePHP structure for parsing messages & their presentation - */ - const STRUCTURE_URI = 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1'; - - /** - * Must reference a "known" plugin, otherwise headers won't display in FirePHP - */ - const PLUGIN_URI = 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3'; - - /** - * Header prefix for Wildfire to recognize & parse headers - */ - const HEADER_PREFIX = 'X-Wf'; - - /** - * Whether or not Wildfire vendor-specific headers have been generated & sent yet - */ - protected static $initialized = false; - - /** - * Shared static message index between potentially multiple handlers - * @var int - */ - protected static $messageIndex = 1; - - protected $sendHeaders = true; - - /** - * Base header creation function used by init headers & record headers - * - * @param array $meta Wildfire Plugin, Protocol & Structure Indexes - * @param string $message Log message - * @return array Complete header string ready for the client as key and message as value - */ - protected function createHeader(array $meta, $message) - { - $header = sprintf('%s-%s', self::HEADER_PREFIX, join('-', $meta)); - - return array($header => $message); - } - - /** - * Creates message header from record - * - * @see createHeader() - * @param array $record - * @return string - */ - protected function createRecordHeader(array $record) - { - // Wildfire is extensible to support multiple protocols & plugins in a single request, - // but we're not taking advantage of that (yet), so we're using "1" for simplicity's sake. - return $this->createHeader( - array(1, 1, 1, self::$messageIndex++), - $record['formatted'] - ); - } - - /** - * {@inheritDoc} - */ - protected function getDefaultFormatter() - { - return new WildfireFormatter(); - } - - /** - * Wildfire initialization headers to enable message parsing - * - * @see createHeader() - * @see sendHeader() - * @return array - */ - protected function getInitHeaders() - { - // Initial payload consists of required headers for Wildfire - return array_merge( - $this->createHeader(array('Protocol', 1), self::PROTOCOL_URI), - $this->createHeader(array(1, 'Structure', 1), self::STRUCTURE_URI), - $this->createHeader(array(1, 'Plugin', 1), self::PLUGIN_URI) - ); - } - - /** - * Send header string to the client - * - * @param string $header - * @param string $content - */ - protected function sendHeader($header, $content) - { - if (!headers_sent() && $this->sendHeaders) { - header(sprintf('%s: %s', $header, $content)); - } - } - - /** - * Creates & sends header for a record, ensuring init headers have been sent prior - * - * @see sendHeader() - * @see sendInitHeaders() - * @param array $record - */ - protected function write(array $record) - { - // WildFire-specific headers must be sent prior to any messages - if (!self::$initialized) { - $this->sendHeaders = $this->headersAccepted(); - - foreach ($this->getInitHeaders() as $header => $content) { - $this->sendHeader($header, $content); - } - - self::$initialized = true; - } - - $header = $this->createRecordHeader($record); - $this->sendHeader(key($header), current($header)); - } - - /** - * Verifies if the headers are accepted by the current user agent - * - * @return Boolean - */ - protected function headersAccepted() - { - return !isset($_SERVER['HTTP_USER_AGENT']) - || preg_match('{\bFirePHP/\d+\.\d+\b}', $_SERVER['HTTP_USER_AGENT']) - || isset($_SERVER['HTTP_X_FIREPHP_VERSION']); - } -} diff --git a/Monolog/Handler/GelfHandler.php b/Monolog/Handler/GelfHandler.php deleted file mode 100755 index 34d48e7..0000000 --- a/Monolog/Handler/GelfHandler.php +++ /dev/null @@ -1,66 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Handler; - -use Gelf\IMessagePublisher; -use Monolog\Logger; -use Monolog\Handler\AbstractProcessingHandler; -use Monolog\Formatter\GelfMessageFormatter; - -/** - * Handler to send messages to a Graylog2 (http://www.graylog2.org) server - * - * @author Matt Lehner - */ -class GelfHandler extends AbstractProcessingHandler -{ - /** - * @var Gelf\IMessagePublisher the publisher object that sends the message to the server - */ - protected $publisher; - - /** - * @param Gelf\IMessagePublisher $publisher a publisher object - * @param integer $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not - */ - public function __construct(IMessagePublisher $publisher, $level = Logger::DEBUG, $bubble = true) - { - parent::__construct($level, $bubble); - - $this->publisher = $publisher; - } - - /** - * {@inheritdoc} - */ - public function close() - { - $this->publisher = null; - } - - /** - * {@inheritdoc} - */ - protected function write(array $record) - { - $this->publisher->publish($record['formatted']); - } - - /** - * {@inheritDoc} - */ - protected function getDefaultFormatter() - { - return new GelfMessageFormatter(); - } -} diff --git a/Monolog/Handler/GroupHandler.php b/Monolog/Handler/GroupHandler.php deleted file mode 100755 index cd29531..0000000 --- a/Monolog/Handler/GroupHandler.php +++ /dev/null @@ -1,74 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Handler; - -/** - * Forwards records to multiple handlers - * - * @author Lenar Lõhmus - */ -class GroupHandler extends AbstractHandler -{ - protected $handlers; - - /** - * @param array $handlers Array of Handlers. - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not - */ - public function __construct(array $handlers, $bubble = true) - { - foreach ($handlers as $handler) { - if (!$handler instanceof HandlerInterface) { - throw new \InvalidArgumentException('The first argument of the GroupHandler must be an array of HandlerInterface instances.'); - } - } - - $this->handlers = $handlers; - $this->bubble = $bubble; - } - - /** - * {@inheritdoc} - */ - public function isHandling(array $record) - { - foreach ($this->handlers as $handler) { - if ($handler->isHandling($record)) { - return true; - } - } - - return false; - } - - /** - * {@inheritdoc} - */ - public function handle(array $record) - { - foreach ($this->handlers as $handler) { - $handler->handle($record); - } - - return false === $this->bubble; - } - - /** - * {@inheritdoc} - */ - public function handleBatch(array $records) - { - foreach ($this->handlers as $handler) { - $handler->handleBatch($records); - } - } -} diff --git a/Monolog/Handler/HandlerInterface.php b/Monolog/Handler/HandlerInterface.php deleted file mode 100755 index d24dc77..0000000 --- a/Monolog/Handler/HandlerInterface.php +++ /dev/null @@ -1,77 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Handler; - -use Monolog\Formatter\FormatterInterface; - -/** - * Interface that all Monolog Handlers must implement - * - * @author Jordi Boggiano - */ -interface HandlerInterface -{ - /** - * Checks whether the given record will be handled by this handler. - * - * This is mostly done for performance reasons, to avoid calling processors for nothing. - * - * @return Boolean - */ - public function isHandling(array $record); - - /** - * Handles a record. - * - * The return value of this function controls the bubbling process of the handler stack. - * - * @param array $record The record to handle - * @return Boolean True means that this handler handled the record, and that bubbling is not permitted. - * False means the record was either not processed or that this handler allows bubbling. - */ - public function handle(array $record); - - /** - * Handles a set of records at once. - * - * @param array $records The records to handle (an array of record arrays) - */ - public function handleBatch(array $records); - - /** - * Adds a processor in the stack. - * - * @param callable $callback - */ - public function pushProcessor($callback); - - /** - * Removes the processor on top of the stack and returns it. - * - * @return callable - */ - public function popProcessor(); - - /** - * Sets the formatter. - * - * @param FormatterInterface $formatter - */ - public function setFormatter(FormatterInterface $formatter); - - /** - * Gets the formatter. - * - * @return FormatterInterface - */ - public function getFormatter(); -} diff --git a/Monolog/Handler/MailHandler.php b/Monolog/Handler/MailHandler.php deleted file mode 100755 index 8629272..0000000 --- a/Monolog/Handler/MailHandler.php +++ /dev/null @@ -1,55 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Handler; - -/** - * Base class for all mail handlers - * - * @author Gyula Sallai - */ -abstract class MailHandler extends AbstractProcessingHandler -{ - /** - * {@inheritdoc} - */ - public function handleBatch(array $records) - { - $messages = array(); - - foreach ($records as $record) { - if ($record['level'] < $this->level) { - continue; - } - $messages[] = $this->processRecord($record); - } - - if (!empty($messages)) { - $this->send((string) $this->getFormatter()->formatBatch($messages), $messages); - } - } - - /** - * Send a mail with the given content - * - * @param string $content - * @param array $records the array of log records that formed this content - */ - abstract protected function send($content, array $records); - - /** - * {@inheritdoc} - */ - protected function write(array $record) - { - $this->send((string) $record['formatted'], array($record)); - } -} diff --git a/Monolog/Handler/MongoDBHandler.php b/Monolog/Handler/MongoDBHandler.php deleted file mode 100755 index 210bb19..0000000 --- a/Monolog/Handler/MongoDBHandler.php +++ /dev/null @@ -1,51 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Handler; - -use Monolog\Logger; -use Monolog\Formatter\NormalizerFormatter; - -/** - * Logs to a MongoDB database. - * - * usage example: - * - * $log = new Logger('application'); - * $mongodb = new MongoDBHandler(new \Mongo("mongodb://localhost:27017"), "logs", "prod"); - * $log->pushHandler($mongodb); - * - * @author Thomas Tourlourat - */ -class MongoDBHandler extends AbstractProcessingHandler -{ - private $mongoCollection; - - public function __construct(\Mongo $mongo, $database, $collection, $level = Logger::DEBUG, $bubble = true) - { - $this->mongoCollection = $mongo->selectCollection($database, $collection); - - parent::__construct($level, $bubble); - } - - protected function write(array $record) - { - $this->mongoCollection->save($record["formatted"]); - } - - /** - * {@inheritDoc} - */ - protected function getDefaultFormatter() - { - return new NormalizerFormatter(); - } -} diff --git a/Monolog/Handler/NativeMailerHandler.php b/Monolog/Handler/NativeMailerHandler.php deleted file mode 100755 index c954de5..0000000 --- a/Monolog/Handler/NativeMailerHandler.php +++ /dev/null @@ -1,65 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Handler; - -use Monolog\Logger; - -/** - * NativeMailerHandler uses the mail() function to send the emails - * - * @author Christophe Coevoet - */ -class NativeMailerHandler extends MailHandler -{ - protected $to; - protected $subject; - protected $headers = array( - 'Content-type: text/plain; charset=utf-8' - ); - - /** - * @param string|array $to The receiver of the mail - * @param string $subject The subject of the mail - * @param string $from The sender of the mail - * @param integer $level The minimum logging level at which this handler will be triggered - * @param boolean $bubble Whether the messages that are handled can bubble up the stack or not - */ - public function __construct($to, $subject, $from, $level = Logger::ERROR, $bubble = true) - { - parent::__construct($level, $bubble); - $this->to = is_array($to) ? $to : array($to); - $this->subject = $subject; - $this->headers[] = sprintf('From: %s', $from); - } - - /** - * @param string|array $header Custom added headers - */ - public function addHeader($headers) - { - if (is_array($headers)) { - $this->headers = array_merge($this->headers, $headers); - } else { - $this->headers[] = $headers; - } - } - - /** - * {@inheritdoc} - */ - protected function send($content, array $records) - { - foreach ($this->to as $to) { - mail($to, $this->subject, wordwrap($content, 70), implode("\r\n", $this->headers) . "\r\n"); - } - } -} diff --git a/Monolog/Handler/NullHandler.php b/Monolog/Handler/NullHandler.php deleted file mode 100755 index 3754e45..0000000 --- a/Monolog/Handler/NullHandler.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Handler; - -use Monolog\Logger; - -/** - * Blackhole - * - * Any record it can handle will be thrown away. This can be used - * to put on top of an existing stack to override it temporarily. - * - * @author Jordi Boggiano - */ -class NullHandler extends AbstractHandler -{ - /** - * @param integer $level The minimum logging level at which this handler will be triggered - */ - public function __construct($level = Logger::DEBUG) - { - parent::__construct($level, false); - } - - /** - * {@inheritdoc} - */ - public function handle(array $record) - { - if ($record['level'] < $this->level) { - return false; - } - - return true; - } -} diff --git a/Monolog/Handler/RotatingFileHandler.php b/Monolog/Handler/RotatingFileHandler.php deleted file mode 100755 index 682542c..0000000 --- a/Monolog/Handler/RotatingFileHandler.php +++ /dev/null @@ -1,109 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Handler; - -use Monolog\Logger; - -/** - * Stores logs to files that are rotated every day and a limited number of files are kept. - * - * This rotation is only intended to be used as a workaround. Using logrotate to - * handle the rotation is strongly encouraged when you can use it. - * - * @author Christophe Coevoet - */ -class RotatingFileHandler extends StreamHandler -{ - protected $filename; - protected $maxFiles; - protected $mustRotate; - - /** - * @param string $filename - * @param integer $maxFiles The maximal amount of files to keep (0 means unlimited) - * @param integer $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not - */ - public function __construct($filename, $maxFiles = 0, $level = Logger::DEBUG, $bubble = true) - { - $this->filename = $filename; - $this->maxFiles = (int) $maxFiles; - - $fileInfo = pathinfo($this->filename); - $timedFilename = $fileInfo['dirname'].'/'.$fileInfo['filename'].'-'.date('Y-m-d'); - if (!empty($fileInfo['extension'])) { - $timedFilename .= '.'.$fileInfo['extension']; - } - - // disable rotation upfront if files are unlimited - if (0 === $this->maxFiles) { - $this->mustRotate = false; - } - - parent::__construct($timedFilename, $level, $bubble); - } - - /** - * {@inheritdoc} - */ - public function close() - { - parent::close(); - - if (true === $this->mustRotate) { - $this->rotate(); - } - } - - /** - * {@inheritdoc} - */ - protected function write(array $record) - { - // on the first record written, if the log is new, we should rotate (once per day) - if (null === $this->mustRotate) { - $this->mustRotate = !file_exists($this->url); - } - - parent::write($record); - } - - /** - * Rotates the files. - */ - protected function rotate() - { - $fileInfo = pathinfo($this->filename); - $glob = $fileInfo['dirname'].'/'.$fileInfo['filename'].'-*'; - if (!empty($fileInfo['extension'])) { - $glob .= '.'.$fileInfo['extension']; - } - $iterator = new \GlobIterator($glob); - $count = $iterator->count(); - if ($this->maxFiles >= $count) { - // no files to remove - return; - } - - // Sorting the files by name to remove the older ones - $array = iterator_to_array($iterator); - usort($array, function($a, $b) { - return strcmp($b->getFilename(), $a->getFilename()); - }); - - foreach (array_slice($array, $this->maxFiles) as $file) { - if ($file->isWritable()) { - unlink($file->getRealPath()); - } - } - } -} diff --git a/Monolog/Handler/SocketHandler.php b/Monolog/Handler/SocketHandler.php deleted file mode 100755 index b44fad7..0000000 --- a/Monolog/Handler/SocketHandler.php +++ /dev/null @@ -1,272 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Handler; - -use Monolog\Logger; - -/** - * Stores to any socket - uses fsockopen() or pfsockopen(). - * - * @author Pablo de Leon Belloc - * @see http://php.net/manual/en/function.fsockopen.php - */ -class SocketHandler extends AbstractProcessingHandler -{ - private $connectionString; - private $connectionTimeout; - private $resource; - private $timeout = 0; - private $persistent = false; - private $errno; - private $errstr; - - /** - * @param string $connectionString Socket connection string - * @param integer $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not - */ - public function __construct($connectionString, $level = Logger::DEBUG, $bubble = true) - { - parent::__construct($level, $bubble); - $this->connectionString = $connectionString; - $this->connectionTimeout = (float) ini_get('default_socket_timeout'); - } - - /** - * Connect (if necessary) and write to the socket - * - * @param array $record - * - * @throws \UnexpectedValueException - * @throws \RuntimeException - */ - public function write(array $record) - { - $this->connectIfNotConnected(); - $this->writeToSocket((string) $record['formatted']); - } - - /** - * We will not close a PersistentSocket instance so it can be reused in other requests. - */ - public function close() - { - if (!$this->isPersistent()) { - $this->closeSocket(); - } - } - - /** - * Close socket, if open - */ - public function closeSocket() - { - if (is_resource($this->resource)) { - fclose($this->resource); - $this->resource = null; - } - } - - /** - * Set socket connection to nbe persistent. It only has effect before the connection is initiated. - * - * @param type $boolean - */ - public function setPersistent($boolean) - { - $this->persistent = (boolean) $boolean; - } - - /** - * Set connection timeout. Only has effect before we connect. - * - * @param integer $seconds - * - * @see http://php.net/manual/en/function.fsockopen.php - */ - public function setConnectionTimeout($seconds) - { - $this->validateTimeout($seconds); - $this->connectionTimeout = (float) $seconds; - } - - /** - * Set write timeout. Only has effect before we connect. - * - * @param type $seconds - * - * @see http://php.net/manual/en/function.stream-set-timeout.php - */ - public function setTimeout($seconds) - { - $this->validateTimeout($seconds); - $this->timeout = (int) $seconds; - } - - /** - * Get current connection string - * - * @return string - */ - public function getConnectionString() - { - return $this->connectionString; - } - - /** - * Get persistent setting - * - * @return boolean - */ - public function isPersistent() - { - return $this->persistent; - } - - /** - * Get current connection timeout setting - * - * @return float - */ - public function getConnectionTimeout() - { - return $this->connectionTimeout; - } - - /** - * Get current in-transfer timeout - * - * @return float - */ - public function getTimeout() - { - return $this->timeout; - } - - /** - * Check to see if the socket is currently available. - * - * UDP might appear to be connected but might fail when writing. See http://php.net/fsockopen for details. - * - * @return boolean - */ - public function isConnected() - { - return is_resource($this->resource) - && !feof($this->resource); // on TCP - other party can close connection. - } - - /** - * Wrapper to allow mocking - */ - protected function pfsockopen() - { - return @pfsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); - } - - /** - * Wrapper to allow mocking - */ - protected function fsockopen() - { - return @fsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); - } - - /** - * Wrapper to allow mocking - */ - protected function streamSetTimeout() - { - return stream_set_timeout($this->resource, $this->timeout); - } - - /** - * Wrapper to allow mocking - */ - protected function fwrite($data) - { - return @fwrite($this->resource, $data); - } - - /** - * Wrapper to allow mocking - */ - protected function streamGetMetadata() - { - return stream_get_meta_data($this->resource); - } - - private function validateTimeout($value) - { - $ok = filter_var($value, FILTER_VALIDATE_INT, array('options' => array( - 'min_range' => 0, - ))); - if ($ok === false) { - throw new \InvalidArgumentException("Timeout must be 0 or a positive integer (got $value)"); - } - } - - private function connectIfNotConnected() - { - if ($this->isConnected()) { - return; - } - $this->connect(); - } - - private function connect() - { - $this->createSocketResource(); - $this->setSocketTimeout(); - } - - private function createSocketResource() - { - if ($this->isPersistent()) { - $resource = $this->pfsockopen(); - } else { - $resource = $this->fsockopen(); - } - if (!$resource) { - throw new \UnexpectedValueException("Failed connecting to $this->connectionString ($this->errno: $this->errstr)"); - } - $this->resource = $resource; - } - - private function setSocketTimeout() - { - if (!$this->streamSetTimeout()) { - throw new \UnexpectedValueException("Failed setting timeout with stream_set_timeout()"); - } - } - - private function writeToSocket($data) - { - $length = strlen($data); - $sent = 0; - while ($this->isConnected() && $sent < $length) { - $chunk = $this->fwrite(substr($data, $sent)); - if ($chunk === false) { - throw new \RuntimeException("Could not write to socket"); - } - $sent += $chunk; - $socketInfo = $this->streamGetMetadata(); - if ($socketInfo['timed_out']) { - throw new \RuntimeException("Write timed-out"); - } - } - if (!$this->isConnected() && $sent < $length) { - throw new \RuntimeException("End-of-file reached, probably we got disconnected (sent $sent of $length)"); - } - } - -} diff --git a/Monolog/Handler/StreamHandler.php b/Monolog/Handler/StreamHandler.php deleted file mode 100755 index a437030..0000000 --- a/Monolog/Handler/StreamHandler.php +++ /dev/null @@ -1,71 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Handler; - -use Monolog\Logger; - -/** - * Stores to any stream resource - * - * Can be used to store into php://stderr, remote and local files, etc. - * - * @author Jordi Boggiano - */ -class StreamHandler extends AbstractProcessingHandler -{ - protected $stream; - protected $url; - - /** - * @param string $stream - * @param integer $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not - */ - public function __construct($stream, $level = Logger::DEBUG, $bubble = true) - { - parent::__construct($level, $bubble); - if (is_resource($stream)) { - $this->stream = $stream; - } else { - $this->url = $stream; - } - } - - /** - * {@inheritdoc} - */ - public function close() - { - if (is_resource($this->stream)) { - fclose($this->stream); - } - $this->stream = null; - } - - /** - * {@inheritdoc} - */ - protected function write(array $record) - { - if (null === $this->stream) { - if (!$this->url) { - throw new \LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().'); - } - $this->stream = @fopen($this->url, 'a'); - if (!is_resource($this->stream)) { - $this->stream = null; - throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened; it may be invalid or not writable.', $this->url)); - } - } - fwrite($this->stream, (string) $record['formatted']); - } -} diff --git a/Monolog/Handler/SwiftMailerHandler.php b/Monolog/Handler/SwiftMailerHandler.php deleted file mode 100755 index 56bf9a2..0000000 --- a/Monolog/Handler/SwiftMailerHandler.php +++ /dev/null @@ -1,55 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Handler; - -use Monolog\Logger; - -/** - * SwiftMailerHandler uses Swift_Mailer to send the emails - * - * @author Gyula Sallai - */ -class SwiftMailerHandler extends MailHandler -{ - protected $mailer; - protected $message; - - /** - * @param \Swift_Mailer $mailer The mailer to use - * @param callback|\Swift_Message $message An example message for real messages, only the body will be replaced - * @param integer $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not - */ - public function __construct(\Swift_Mailer $mailer, $message, $level = Logger::ERROR, $bubble = true) - { - parent::__construct($level, $bubble); - $this->mailer = $mailer; - if (!$message instanceof \Swift_Message && is_callable($message)) { - $message = call_user_func($message); - } - if (!$message instanceof \Swift_Message) { - throw new \InvalidArgumentException('You must provide either a Swift_Message instance or a callback returning it'); - } - $this->message = $message; - } - - /** - * {@inheritdoc} - */ - protected function send($content, array $records) - { - $message = clone $this->message; - $message->setBody($content); - - $this->mailer->send($message); - } -} diff --git a/Monolog/Handler/SyslogHandler.php b/Monolog/Handler/SyslogHandler.php deleted file mode 100755 index ed2fe44..0000000 --- a/Monolog/Handler/SyslogHandler.php +++ /dev/null @@ -1,110 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Handler; - -use Monolog\Logger; - -/** - * Logs to syslog service. - * - * usage example: - * - * $log = new Logger('application'); - * $syslog = new SyslogHandler('myfacility', 'local6'); - * $formatter = new LineFormatter("%channel%.%level_name%: %message% %extra%"); - * $syslog->setFormatter($formatter); - * $log->pushHandler($syslog); - * - * @author Sven Paulus - */ -class SyslogHandler extends AbstractProcessingHandler -{ - /** - * Translates Monolog log levels to syslog log priorities. - */ - private $logLevels = array( - Logger::DEBUG => LOG_DEBUG, - Logger::INFO => LOG_INFO, - Logger::NOTICE => LOG_NOTICE, - Logger::WARNING => LOG_WARNING, - Logger::ERROR => LOG_ERR, - Logger::CRITICAL => LOG_CRIT, - Logger::ALERT => LOG_ALERT, - Logger::EMERGENCY => LOG_EMERG, - ); - - /** - * List of valid log facility names. - */ - private $facilities = array( - 'auth' => LOG_AUTH, - 'authpriv' => LOG_AUTHPRIV, - 'cron' => LOG_CRON, - 'daemon' => LOG_DAEMON, - 'kern' => LOG_KERN, - 'lpr' => LOG_LPR, - 'mail' => LOG_MAIL, - 'news' => LOG_NEWS, - 'syslog' => LOG_SYSLOG, - 'user' => LOG_USER, - 'uucp' => LOG_UUCP, - ); - - /** - * @param string $ident - * @param mixed $facility - * @param integer $level The minimum logging level at which this handler will be triggered - * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not - */ - public function __construct($ident, $facility = LOG_USER, $level = Logger::DEBUG, $bubble = true) - { - parent::__construct($level, $bubble); - - if (!defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->facilities['local0'] = LOG_LOCAL0; - $this->facilities['local1'] = LOG_LOCAL1; - $this->facilities['local2'] = LOG_LOCAL2; - $this->facilities['local3'] = LOG_LOCAL3; - $this->facilities['local4'] = LOG_LOCAL4; - $this->facilities['local5'] = LOG_LOCAL5; - $this->facilities['local6'] = LOG_LOCAL6; - $this->facilities['local7'] = LOG_LOCAL7; - } - - // convert textual description of facility to syslog constant - if (array_key_exists(strtolower($facility), $this->facilities)) { - $facility = $this->facilities[strtolower($facility)]; - } elseif (!in_array($facility, array_values($this->facilities), true)) { - throw new \UnexpectedValueException('Unknown facility value "'.$facility.'" given'); - } - - if (!openlog($ident, LOG_PID, $facility)) { - throw new \LogicException('Can\'t open syslog for ident "'.$ident.'" and facility "'.$facility.'"'); - } - } - - /** - * {@inheritdoc} - */ - public function close() - { - closelog(); - } - - /** - * {@inheritdoc} - */ - protected function write(array $record) - { - syslog($this->logLevels[$record['level']], (string) $record['formatted']); - } -} diff --git a/Monolog/Handler/TestHandler.php b/Monolog/Handler/TestHandler.php deleted file mode 100755 index 085d9e1..0000000 --- a/Monolog/Handler/TestHandler.php +++ /dev/null @@ -1,140 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Handler; - -use Monolog\Logger; - -/** - * Used for testing purposes. - * - * It records all records and gives you access to them for verification. - * - * @author Jordi Boggiano - */ -class TestHandler extends AbstractProcessingHandler -{ - protected $records = array(); - protected $recordsByLevel = array(); - - public function getRecords() - { - return $this->records; - } - - public function hasEmergency($record) - { - return $this->hasRecord($record, Logger::EMERGENCY); - } - - public function hasAlert($record) - { - return $this->hasRecord($record, Logger::ALERT); - } - - public function hasCritical($record) - { - return $this->hasRecord($record, Logger::CRITICAL); - } - - public function hasError($record) - { - return $this->hasRecord($record, Logger::ERROR); - } - - public function hasWarning($record) - { - return $this->hasRecord($record, Logger::WARNING); - } - - public function hasNotice($record) - { - return $this->hasRecord($record, Logger::NOTICE); - } - - public function hasInfo($record) - { - return $this->hasRecord($record, Logger::INFO); - } - - public function hasDebug($record) - { - return $this->hasRecord($record, Logger::DEBUG); - } - - public function hasEmergencyRecords() - { - return isset($this->recordsByLevel[Logger::EMERGENCY]); - } - - public function hasAlertRecords() - { - return isset($this->recordsByLevel[Logger::ALERT]); - } - - public function hasCriticalRecords() - { - return isset($this->recordsByLevel[Logger::CRITICAL]); - } - - public function hasErrorRecords() - { - return isset($this->recordsByLevel[Logger::ERROR]); - } - - public function hasWarningRecords() - { - return isset($this->recordsByLevel[Logger::WARNING]); - } - - public function hasNoticeRecords() - { - return isset($this->recordsByLevel[Logger::NOTICE]); - } - - public function hasInfoRecords() - { - return isset($this->recordsByLevel[Logger::INFO]); - } - - public function hasDebugRecords() - { - return isset($this->recordsByLevel[Logger::DEBUG]); - } - - protected function hasRecord($record, $level) - { - if (!isset($this->recordsByLevel[$level])) { - return false; - } - - if (is_array($record)) { - $record = $record['message']; - } - - foreach ($this->recordsByLevel[$level] as $rec) { - if ($rec['message'] === $record) { - return true; - } - } - - return false; - } - - /** - * {@inheritdoc} - */ - protected function write(array $record) - { - $this->recordsByLevel[$record['level']][] = $record; - $this->records[] = $record; - } -} diff --git a/Monolog/Logger.php b/Monolog/Logger.php deleted file mode 100755 index 0ff2aa8..0000000 --- a/Monolog/Logger.php +++ /dev/null @@ -1,466 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog; - -use Monolog\Handler\HandlerInterface; -use Monolog\Handler\StreamHandler; - -/** - * Monolog log channel - * - * It contains a stack of Handlers and a stack of Processors, - * and uses them to store records that are added to it. - * - * @author Jordi Boggiano - */ -class Logger -{ - /** - * Detailed debug information - */ - const DEBUG = 100; - - /** - * Interesting events - * - * Examples: User logs in, SQL logs. - */ - const INFO = 200; - - /** - * Uncommon events - */ - const NOTICE = 250; - - /** - * Exceptional occurrences that are not errors - * - * Examples: Use of deprecated APIs, poor use of an API, - * undesirable things that are not necessarily wrong. - */ - const WARNING = 300; - - /** - * Runtime errors - */ - const ERROR = 400; - - /** - * Critical conditions - * - * Example: Application component unavailable, unexpected exception. - */ - const CRITICAL = 500; - - /** - * Action must be taken immediately - * - * Example: Entire website down, database unavailable, etc. - * This should trigger the SMS alerts and wake you up. - */ - const ALERT = 550; - - /** - * Urgent alert. - */ - const EMERGENCY = 600; - - protected static $levels = array( - 100 => 'DEBUG', - 200 => 'INFO', - 250 => 'NOTICE', - 300 => 'WARNING', - 400 => 'ERROR', - 500 => 'CRITICAL', - 550 => 'ALERT', - 600 => 'EMERGENCY', - ); - - /** - * @var DateTimeZone - */ - protected static $timezone; - - protected $name; - - /** - * The handler stack - * - * @var array of Monolog\Handler\HandlerInterface - */ - protected $handlers = array(); - - protected $processors = array(); - - /** - * @param string $name The logging channel - */ - public function __construct($name) - { - $this->name = $name; - - if (!self::$timezone) { - self::$timezone = new \DateTimeZone(date_default_timezone_get() ?: 'UTC'); - } - } - - /** - * @return string - */ - public function getName() - { - return $this->name; - } - - /** - * Pushes a handler on to the stack. - * - * @param HandlerInterface $handler - */ - public function pushHandler(HandlerInterface $handler) - { - array_unshift($this->handlers, $handler); - } - - /** - * Pops a handler from the stack - * - * @return HandlerInterface - */ - public function popHandler() - { - if (!$this->handlers) { - throw new \LogicException('You tried to pop from an empty handler stack.'); - } - - return array_shift($this->handlers); - } - - /** - * Adds a processor on to the stack. - * - * @param callable $callback - */ - public function pushProcessor($callback) - { - if (!is_callable($callback)) { - throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), '.var_export($callback, true).' given'); - } - array_unshift($this->processors, $callback); - } - - /** - * Removes the processor on top of the stack and returns it. - * - * @return callable - */ - public function popProcessor() - { - if (!$this->processors) { - throw new \LogicException('You tried to pop from an empty processor stack.'); - } - - return array_shift($this->processors); - } - - /** - * Adds a log record. - * - * @param integer $level The logging level - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed - */ - public function addRecord($level, $message, array $context = array()) - { - if (!$this->handlers) { - $this->pushHandler(new StreamHandler('php://stderr', self::DEBUG)); - } - $record = array( - 'message' => (string) $message, - 'context' => $context, - 'level' => $level, - 'level_name' => self::getLevelName($level), - 'channel' => $this->name, - 'datetime' => \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true)))->setTimeZone(self::$timezone), - 'extra' => array(), - ); - // check if any message will handle this message - $handlerKey = null; - foreach ($this->handlers as $key => $handler) { - if ($handler->isHandling($record)) { - $handlerKey = $key; - break; - } - } - // none found - if (null === $handlerKey) { - return false; - } - // found at least one, process message and dispatch it - foreach ($this->processors as $processor) { - $record = call_user_func($processor, $record); - } - while (isset($this->handlers[$handlerKey]) && - false === $this->handlers[$handlerKey]->handle($record)) { - $handlerKey++; - } - - return true; - } - - /** - * Adds a log record at the DEBUG level. - * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed - */ - public function addDebug($message, array $context = array()) - { - return $this->addRecord(self::DEBUG, $message, $context); - } - - /** - * Adds a log record at the INFO level. - * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed - */ - public function addInfo($message, array $context = array()) - { - return $this->addRecord(self::INFO, $message, $context); - } - - /** - * Adds a log record at the NOTICE level. - * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed - */ - public function addNotice($message, array $context = array()) - { - return $this->addRecord(self::NOTICE, $message, $context); - } - - /** - * Adds a log record at the WARNING level. - * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed - */ - public function addWarning($message, array $context = array()) - { - return $this->addRecord(self::WARNING, $message, $context); - } - - /** - * Adds a log record at the ERROR level. - * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed - */ - public function addError($message, array $context = array()) - { - return $this->addRecord(self::ERROR, $message, $context); - } - - /** - * Adds a log record at the CRITICAL level. - * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed - */ - public function addCritical($message, array $context = array()) - { - return $this->addRecord(self::CRITICAL, $message, $context); - } - - /** - * Adds a log record at the ALERT level. - * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed - */ - public function addAlert($message, array $context = array()) - { - return $this->addRecord(self::ALERT, $message, $context); - } - - /** - * Adds a log record at the EMERGENCY level. - * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed - */ - public function addEmergency($message, array $context = array()) - { - return $this->addRecord(self::EMERGENCY, $message, $context); - } - - /** - * Gets the name of the logging level. - * - * @param integer $level - * @return string - */ - public static function getLevelName($level) - { - return self::$levels[$level]; - } - - /** - * Checks whether the Logger has a handler that listens on the given level - * - * @param integer $level - * @return Boolean - */ - public function isHandling($level) - { - $record = array( - 'message' => '', - 'context' => array(), - 'level' => $level, - 'level_name' => self::getLevelName($level), - 'channel' => $this->name, - 'datetime' => new \DateTime(), - 'extra' => array(), - ); - - foreach ($this->handlers as $key => $handler) { - if ($handler->isHandling($record)) { - return true; - } - } - - return false; - } - - /** - * Adds a log record at the DEBUG level. - * - * This method allows for compatibility with common interfaces. - * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed - */ - public function debug($message, array $context = array()) - { - return $this->addRecord(self::DEBUG, $message, $context); - } - - /** - * Adds a log record at the INFO level. - * - * This method allows for compatibility with common interfaces. - * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed - */ - public function info($message, array $context = array()) - { - return $this->addRecord(self::INFO, $message, $context); - } - - /** - * Adds a log record at the INFO level. - * - * This method allows for compatibility with common interfaces. - * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed - */ - public function notice($message, array $context = array()) - { - return $this->addRecord(self::NOTICE, $message, $context); - } - - /** - * Adds a log record at the WARNING level. - * - * This method allows for compatibility with common interfaces. - * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed - */ - public function warn($message, array $context = array()) - { - return $this->addRecord(self::WARNING, $message, $context); - } - - /** - * Adds a log record at the ERROR level. - * - * This method allows for compatibility with common interfaces. - * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed - */ - public function err($message, array $context = array()) - { - return $this->addRecord(self::ERROR, $message, $context); - } - - /** - * Adds a log record at the CRITICAL level. - * - * This method allows for compatibility with common interfaces. - * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed - */ - public function crit($message, array $context = array()) - { - return $this->addRecord(self::CRITICAL, $message, $context); - } - - /** - * Adds a log record at the ALERT level. - * - * This method allows for compatibility with common interfaces. - * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed - */ - public function alert($message, array $context = array()) - { - return $this->addRecord(self::ALERT, $message, $context); - } - - /** - * Adds a log record at the EMERGENCY level. - * - * This method allows for compatibility with common interfaces. - * - * @param string $message The log message - * @param array $context The log context - * @return Boolean Whether the record has been processed - */ - public function emerg($message, array $context = array()) - { - return $this->addRecord(self::EMERGENCY, $message, $context); - } -} diff --git a/Monolog/Processor/IntrospectionProcessor.php b/Monolog/Processor/IntrospectionProcessor.php deleted file mode 100755 index b126218..0000000 --- a/Monolog/Processor/IntrospectionProcessor.php +++ /dev/null @@ -1,58 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Processor; - -/** - * Injects line/file:class/function where the log message came from - * - * Warning: This only works if the handler processes the logs directly. - * If you put the processor on a handler that is behind a FingersCrossedHandler - * for example, the processor will only be called once the trigger level is reached, - * and all the log records will have the same file/line/.. data from the call that - * triggered the FingersCrossedHandler. - * - * @author Jordi Boggiano - */ -class IntrospectionProcessor -{ - /** - * @param array $record - * @return array - */ - public function __invoke(array $record) - { - $trace = debug_backtrace(); - - // skip first since it's always the current method - array_shift($trace); - // the call_user_func call is also skipped - array_shift($trace); - - $i = 0; - while (isset($trace[$i]['class']) && false !== strpos($trace[$i]['class'], 'Monolog\\')) { - $i++; - } - - // we should have the call source now - $record['extra'] = array_merge( - $record['extra'], - array( - 'file' => isset($trace[$i-1]['file']) ? $trace[$i-1]['file'] : null, - 'line' => isset($trace[$i-1]['line']) ? $trace[$i-1]['line'] : null, - 'class' => isset($trace[$i]['class']) ? $trace[$i]['class'] : null, - 'function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : null, - ) - ); - - return $record; - } -} diff --git a/Monolog/Processor/MemoryPeakUsageProcessor.php b/Monolog/Processor/MemoryPeakUsageProcessor.php deleted file mode 100755 index e48672b..0000000 --- a/Monolog/Processor/MemoryPeakUsageProcessor.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Processor; - -/** - * Injects memory_get_peak_usage in all records - * - * @see Monolog\Processor\MemoryProcessor::__construct() for options - * @author Rob Jensen - */ -class MemoryPeakUsageProcessor extends MemoryProcessor -{ - /** - * @param array $record - * @return array - */ - public function __invoke(array $record) - { - $bytes = memory_get_peak_usage($this->realUsage); - $formatted = self::formatBytes($bytes); - - $record['extra'] = array_merge( - $record['extra'], - array( - 'memory_peak_usage' => $formatted, - ) - ); - - return $record; - } -} diff --git a/Monolog/Processor/MemoryProcessor.php b/Monolog/Processor/MemoryProcessor.php deleted file mode 100755 index 7551043..0000000 --- a/Monolog/Processor/MemoryProcessor.php +++ /dev/null @@ -1,50 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Processor; - -/** - * Some methods that are common for all memory processors - * - * @author Rob Jensen - */ -abstract class MemoryProcessor -{ - protected $realUsage; - - /** - * @param boolean $realUsage - */ - public function __construct($realUsage = true) - { - $this->realUsage = (boolean) $realUsage; - } - - /** - * Formats bytes into a human readable string - * - * @param int $bytes - * @return string - */ - protected static function formatBytes($bytes) - { - $bytes = (int) $bytes; - - if ($bytes > 1024*1024) { - return round($bytes/1024/1024, 2).' MB'; - } elseif ($bytes > 1024) { - return round($bytes/1024, 2).' KB'; - } - - return $bytes . ' B'; - } - -} diff --git a/Monolog/Processor/MemoryUsageProcessor.php b/Monolog/Processor/MemoryUsageProcessor.php deleted file mode 100755 index 2c4a807..0000000 --- a/Monolog/Processor/MemoryUsageProcessor.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Processor; - -/** - * Injects memory_get_usage in all records - * - * @see Monolog\Processor\MemoryProcessor::__construct() for options - * @author Rob Jensen - */ -class MemoryUsageProcessor extends MemoryProcessor -{ - /** - * @param array $record - * @return array - */ - public function __invoke(array $record) - { - $bytes = memory_get_usage($this->realUsage); - $formatted = self::formatBytes($bytes); - - $record['extra'] = array_merge( - $record['extra'], - array( - 'memory_usage' => $formatted, - ) - ); - - return $record; - } -} diff --git a/Monolog/Processor/WebProcessor.php b/Monolog/Processor/WebProcessor.php deleted file mode 100755 index e297c23..0000000 --- a/Monolog/Processor/WebProcessor.php +++ /dev/null @@ -1,66 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Monolog\Processor; - -/** - * Injects url/method and remote IP of the current web request in all records - * - * @author Jordi Boggiano - */ -class WebProcessor -{ - protected $serverData; - - /** - * @param mixed $serverData array or object w/ ArrayAccess that provides access to the $_SERVER data - */ - public function __construct($serverData = null) - { - if (null === $serverData) { - $this->serverData =& $_SERVER; - } elseif (is_array($serverData) || $serverData instanceof \ArrayAccess) { - $this->serverData = $serverData; - } else { - throw new \UnexpectedValueException('$serverData must be an array or object implementing ArrayAccess.'); - } - } - - /** - * @param array $record - * @return array - */ - public function __invoke(array $record) - { - // skip processing if for some reason request data - // is not present (CLI or wonky SAPIs) - if (!isset($this->serverData['REQUEST_URI'])) { - return $record; - } - - if (!isset($this->serverData['HTTP_REFERER'])) { - $this->serverData['HTTP_REFERER'] = null; - } - - $record['extra'] = array_merge( - $record['extra'], - array( - 'url' => $this->serverData['REQUEST_URI'], - 'ip' => $this->serverData['REMOTE_ADDR'], - 'http_method' => $this->serverData['REQUEST_METHOD'], - 'server' => $this->serverData['SERVER_NAME'], - 'referrer' => $this->serverData['HTTP_REFERER'], - ) - ); - - return $record; - } -} diff --git a/Trackvia/Api.php b/Trackvia/Api.php old mode 100644 new mode 100755 index d0e6835..602054d --- a/Trackvia/Api.php +++ b/Trackvia/Api.php @@ -1,21 +1,20 @@ request = new Request(); $this->auth = new Authentication($this->request, $params); + + // add an event listener for a new token on the authentication object + $this->auth->on('new_access_token', array($this, 'onNewAccessToken')); } /** @@ -62,6 +65,15 @@ public function authenticate() return $this->auth->authenticate(); } + /** + * Method the handle the new_token even trigger by the authentication class + * Bubble up the event with token data for the client. + */ + public function onNewAccessToken($data) + { + $this->trigger('new_token', $data); + } + /** * Set the tokend ata for authentication * @param [type] $tokenData [description] @@ -71,108 +83,138 @@ public function setTokenData($tokenData) $this->auth->setTokenData($tokenData); } - /** - * Attach an event listener. - * @param string $event - * @param string|array $callback A callback function name that can be passed into call_user_func() - */ - public function on($event, $callback) + public function getAuthentication() { - // if (!array_key_exists($event, $this->events)) { - // throw new Exception("Cannot bind to event \"$event\". This event is not supported."); - // } - // - if (!isset($this->events[$event])) { - // initialize an array for this event name - $this->events[$event] = array(); - } - - if (!is_callable($callback)) { - throw new Exception('Callback cannot is not callable. Check you have the right function name.'); - } - - $this->events[$event][] = $callback; + return $this->auth; } - /** - * Trigger a binded event. - * This will call all binded callback functions for a given event. - * @param string $event The event name - */ - public function trigger($event) + public function getRequest() { - if (!array_key_exists($event, $this->events)) { - throw new Exception("Cannot trigger event \"$event\". This event is not supported."); - } - - // set different args based on event name - switch ($event) { - case 'new_token': - $data = array( - 'access_token' => $this->accessToken, - 'refresh_token' => $this->refresh, - 'expires_at' => $this->expiresAt - ); - break; - } - // loop through each callback for this event - foreach ($this->events[$event] as $callback) { - call_user_func($callback, $data); - } + return $this->request; } /** * Check if the response failed and if the token is expired. - * If it is expired, use the refresh token to get a new one + * + * Any errors returned from the API server will be thrown as an Exception. + * * @param array $response * @return boolean */ - private function checkResponse($response) + private function checkResponse() { + $response = $this->request->getResponse(); if (is_array($response) && isset($response['error_description'])) { - if ($response['error_description'] == self::EXPIRED_ACCESS_TOKEN) { - + switch ($response['error_description']) { + case self::EXPIRED_ACCESS_TOKEN: + $this->isTokenExpired = true; + // return here so we don't throw this error + // so we can use the refresh token + return false; } + + // throw an \Exception with the returned error message + throw new \Exception('API Error :: ' . $response['error_description']); } + + return true; } - private function api($url, $httpMethod = 'GET') + /** + * Make an api request. + * + * @param string $url + * @param string $httpMethod The http method to use with this request + * @param array $data Optional array of data to send with request + * @param string $contentType + * @return array The json parsed response from the server + */ + private function api($url, $httpMethod = 'GET', $data = array(), $contentType = null) { - // check for access token - // if (!$this->auth->hasAccessToken() || $this->auth->isAccessTokenExpired()) { - - // } + // trigger an event + $this->trigger('api_request_init', array('url' => $url)); + $this->authenticate(); $accessToken = $this->auth->getAccessToken(); - if (!$accessToken) { // should have a token at this point // if not, something went wrong - throw new Exception('Cannot make an api request without an access token'); + throw new \Exception('Cannot make an api request without an access token'); } // save this request in case we need to use the refresh token - $this->lastRequest = array( - 'url' => $url - // 'data' => $data + $lastRequest = array( + 'url' => $url, + 'method' => $httpMethod, + 'data' => $data ); - // make the request with current access token - $response = $this->request->request($url, $httpMethod, array( - 'access_token' => $accessToken - )); + // add the access token onto the url + $url = $url . '?access_token='.$accessToken; - $this->trigger('api_request', array('url' => $url)); + $this->trigger('api_request_send', array('url' => $url, 'http_method' => $httpMethod)); + + $this->request + ->setMethod($httpMethod) + ->setData($data); - $this->checkResponse($response); + if ($contentType) { + $this->request->setContentType("application/$contentType"); + } + // now send the request + $this->request->send($url); - if (!$response && $this->request->isTokenExpired()) { - //use refresh token to request new access token - if ($this->authenticate()) { - // redo initial api request - } + $this->trigger('api_request_complete', array('url' => $url, 'response' => $this->request->getResponse())); + + // check the response for any errors + $vaild = $this->checkResponse(); + + if (!$vaild && $this->isTokenExpired) { + // blow out the current token so a new one gets requested + $this->auth->clearAccessToken(); + + // redo the last api request + $this->api( + $lastRequest['url'], + $lastRequest['method'], + $lastRequest['data'] + ); } + + return $this->request->getResponse(); + } + + /** + * Get a list of all dashboards, or a single dashboard. + * + * Optional id parameter lets you specify one dashboard if you want. + * Leave is empty to get all dashboards back. + * + * @param int $id + * @return array Array of dashboard data returned from the api + */ + public function getDashboards($id = null) + { + if ($id != null && !is_int($id)) { + throw new \Exception('Dashboard ID must be an integer'); + } + + // build the url + $url = self::BASE_URL . self::DASHBOARDS_URL . ($id ? '/'.$id : ''); + + return $this->api($url, 'GET'); + } + + /** + * Get data for a single dashboard. + * + * @param int $id + * @return array + */ + public function getDashboard($id) + { + return $this->getDashboards($id); } /** @@ -181,17 +223,17 @@ private function api($url, $httpMethod = 'GET') * Optional app_id parameter lets you specify one app if you want. * Leave is empty to get all apps back. * - * @param integer $appId + * @param int $appId * @return array Array of app data returned from the api */ public function getApps($appId = null) { if ($appId != null && !is_int($appId)) { - throw new Exception('App ID must be an integer'); + throw new \Exception('App ID must be an integer'); } // build the url - $url = self::BASE_URL . self::APPS_URL . $appId; + $url = self::BASE_URL . self::APPS_URL . ($appId ? '/'.$appId : ''); return $this->api($url, 'GET'); } @@ -200,29 +242,29 @@ public function getApps($appId = null) * Get data for a single app by app_id. * This will provide you with all the tables available for this app. * - * @param integer $appId - * @return array Array of app data returned fromt the api + * @param int $appId + * @return array Array of app data returned fromt the api */ public function getApp($appId) { - $this->getApps($appId); + return $this->getApps($appId); } /** * Get table data back for a table_id. * This will provide you all the views available for this table. * - * @param integer $tableId - * @return array Array of table data returned from the api + * @param int $tableId + * @return array Array of table data returned from the api */ public function getTable($tableId) { if (!is_int($tableId)) { - throw new Exception('Table ID must be an integer'); + throw new \Exception('Table ID must be an integer'); } // build the url - $url = self::BASE_URL . self::TABLES_URL . $tableId; + $url = self::BASE_URL . self::TABLES_URL .'/'. $tableId; return $this->api($url, 'GET'); } @@ -231,73 +273,128 @@ public function getTable($tableId) * Get view data back for a view_id. * This will provide you with all the records under this view. * - * @param integer $viewId - * @return array Array of view data returned from the api + * @param int $viewId + * @return array Array of view data returned from the api */ public function getView($viewId) { if (!is_int($viewId)) { - throw new Exception('View ID must be an integer'); + throw new \Exception('View ID must be an integer'); } // build the url - $url = self::BASE_URL . self::VIEWS_URL . $viewId; - - return $this->api($url); + $url = self::BASE_URL . self::VIEWS_URL .'/'. $viewId; + + return $this->api($url, 'GET'); } /** * Get Record data back for a record_id. * This will provide you with all the column data for a record. * - * @param integer $recordId - * @return array Array of Record data returned from the api + * @param int $id + * @return array Array of Record data returned from the api */ - public function getRecord($recordId) + public function getRecord($id) { - if (!is_int($recordId)) { - throw new Exception('Record ID must be an integer'); + if (!is_int($id)) { + throw new \Exception('Record ID must be an integer'); } // build the url - $url = self::BASE_URL . self::RECORDS_URL . $recordId; + $url = self::BASE_URL . self::RECORDS_URL .'/'. $id; - return $this->api($url); + return $this->api($url, 'GET'); } - public function addRecord($data) + /** + * Add a new record to a table + * @param int $id + * @param array $data + * @return array + */ + public function addRecord($id, $data) { - # code... + $url = self::BASE_URL . self::RECORDS_URL; + + $data = array( + array( + 'table_id' => $id, + 'data' => $data + )); + return $this->api($url, 'POST', json_encode($data), 'json'); } + /** + * Add more than one record at once. Batch inserts. + * + * @param array $data + * @return array + */ public function addRecords($data) { - # code... + $url = self::BASE_URL . self::RECORDS_URL; + return $this->api($url, 'POST', json_encode($data), 'json'); } - public function updateRecord($id) + /** + * Update a single record. + * + * @param int $id + * @param array $data + * @return array + */ + public function updateRecord($id, $data) { - # code... + $url = self::BASE_URL . self::RECORDS_URL .'/'. $id; + return $this->api($url, 'PUT', json_encode($data), 'json'); } + /** + * Update multiple records. + * + * @param array $data + * @return array + */ public function updateRecords($data) { - # code... + $url = self::BASE_URL . self::RECORDS_URL; + return $this->api($url, 'PUT', json_encode($data), 'json'); } + /** + * Delete a record by id. + * + * @param int $id + */ public function deleteRecord($id) { - # code... + $url = self::BASE_URL . self::RECORDS_URL .'/'. $id; + return $this->api($url, 'DELETE'); } + /** + * Delete multiple records at once. Batch delete. + * + * @param array $data + */ public function deleteRecords($data) { - # code... + $url = self::BASE_URL . self::RECORDS_URL; + return $this->api($url, 'DELETE', json_encode($data), 'json'); } + /** + * Search for records on a table. + * + * @param int $tableId + * @param string $term + * @return array + */ public function search($tableId, $term) { - # code... + $url = self::BASE_URL . self::SEARCH_URL .'/'. $tableId .'/'. $term; + return $this->api($url, 'GET'); } } \ No newline at end of file diff --git a/Trackvia/Authentication.php b/Trackvia/Authentication.php old mode 100644 new mode 100755 index 7c2e996..46e6a06 --- a/Trackvia/Authentication.php +++ b/Trackvia/Authentication.php @@ -1,7 +1,7 @@ request = $request; if (!is_array($params)) { - throw new Exception('You must pass in your client_id and client_secrect'); + throw new \Exception('You must pass in your client_id and client_secrect'); } if (!isset($params['client_id'])) { - throw new Exception('No client_id provided. This is required.'); + throw new \Exception('No client_id provided. This is required.'); } if (!isset($params['client_secret'])) { - throw new Exception('No client_secrect provided. This is required.'); + throw new \Exception('No client_secrect provided. This is required.'); } $this->clientId = $params['client_id']; @@ -74,8 +76,8 @@ public function __construct(Request $request, $params) /** * Set the user credentials to use for authentication - * @param [type] $username [description] - * @param [type] $password [description] + * @param string $username + * @param string $password */ public function setUserCreds($username, $password) { @@ -122,7 +124,7 @@ public function getTokenData() */ public function hasAccessToken() { - return !empty($this->tokenData) && isset($this->tokenData['access_token']); + return !empty($this->tokenData) && isset($this->tokenData['access_token']) && $this->tokenData['access_token'] != ''; } /** @@ -140,7 +142,7 @@ public function getAccessToken() */ public function hasRefreshToken() { - return !empty($this->tokenData) && isset($this->tokenData['refresh_token']); + return !empty($this->tokenData) && isset($this->tokenData['refresh_token']) && $this->tokenData['refresh_token'] != ''; } /** @@ -149,36 +151,89 @@ public function hasRefreshToken() */ public function getRefreshToken() { - return $this->tokenData['refresh_token']; + $token = $this->tokenData['refresh_token']; + $this->trigger('has_refresh_token', array('refresh_token' => $token)); + return $token; + } + + public function getExpiresAt() + { + return $this->tokenData['expires_at']; } /** - * Clear the current token data + * Clear the current access token by setting it to null. + * Clearing the access token before calling the "authenticate" method + * will force it to get a new access token. */ - private function clearTokenData() + public function clearAccessToken() { - $this->tokenData = null; + if ($this->hasAccessToken()) { + $this->tokenData['access_token'] = null; + } + } + + /** + * Clear access and refresh tokens so that authentication will request a new token + */ + public function clearAllTokens() + { + $this->tokenData = array(); } /** * Check if the current token expired. - * We use the expired_at time that should be set by the client. + * We check the expired_at time that should be set by the client. * @return boolean */ private function isAccessTokenExpired() { - return $this->tokenData['expires_at'] > time(); + if (!isset($this->tokenData['expires_at']) || $this->tokenData['expires_at'] <= time()) { + return true; + } + return false; } /** - * Check if there is an access token and if it is expired base on the expired_at property. + * Check if there is an access token and if it is expired based on the expired_at property. * Not a valid token if either condition fails. * * @return boolean */ private function isAccessTokenValid() { - return $this->hasAccessToken() && !$this->isAccessTokenExpired(); + $retval = $this->hasAccessToken() && !$this->isAccessTokenExpired(); + $this->trigger('is_token_valid', array('is_valid' => $retval)); + return $retval; + } + + /** + * Check if the response failed and if the token is expired. + * + * Any errors returned from the API server will be thrown as an Exception. + * + * @param array $response + * @return boolean + */ + private function checkResponse() + { + $response = $this->request->getResponse(); + $httpCode = $this->request->getResponseCode(); + + if (!$response) { + throw new \Exception('Requesting Access Token failed'); + + return false; + } + + if ($httpCode == 400 && isset($response['error_description'])) { + // throw an Exception with the returned error message + throw new \Exception($response['error_description']); + + return false; + } + + return true; } /** @@ -186,52 +241,81 @@ private function isAccessTokenValid() * * @param string $username The user's username credential * @param string $password The user's password credential - * @return string The access token for the user + * @return array|boolean Array of token data returned from the auth server or false on error */ public function requestTokenWithUserCreds($username, $password) { + $this->trigger('request_token_with_user_creds', array( + 'username' => $username, + 'password' => $password + )); + $url = $this->getTokenUrl(); - $response = $this->request->post($url, array( - 'client_id' => $this->clientId, - 'client_secret' => $this->clientSecret, - 'grant_type' => 'password', - 'username' => $username, - 'password' => $password - )); + $this->request + ->setMethod('post') + ->setContentType('application/x-www-form-urlencoded') + ->setData(array( + 'client_id' => $this->clientId, + 'client_secret' => $this->clientSecret, + 'grant_type' => 'password', + 'username' => $username, + 'password' => $password + )) + ->send($url); - if (!$response) { - throw new Exception('Requesting Access Token failed'); - } + $vaild = $this->checkResponse(); + + if ($vaild) { + $this->tokenData = $this->request->getResponse(); + $this->tokenData['expires_at'] = $this->tokenData['expires_in'] + time(); - $this->tokenData = $response; - $this->tokenData['expires_at'] = $this->tokenData['expires_in'] + time(); + $this->trigger('new_access_token', $this->tokenData); - return $this->tokenData; + return $this->tokenData; + } + + return false; } /** * Get a new access token with a refresh token. * * @param string $refreshToken - * @return array Array of token data returned from the auth server + * @return array|boolean Array of token data returned from the auth server or false on error */ public function requestTokenWithRefreshToken($refreshToken) { + $this->trigger('request_token_with_refresh_token', array( + 'refresh_token' => $refreshToken + )); + // use the refresh token to get a new access token $url = $this->getTokenUrl(); - $response = $this->request->post($url, array( - 'client_id' => $this->clientId, - 'client_secret' => $this->clientSecret, - 'grant_type' => 'refresh_token', - 'refresh_token' => $refreshToken - )); + $this->request + ->setMethod('post') + ->setContentType('application/x-www-form-urlencoded') + ->setData(array( + 'client_id' => $this->clientId, + 'client_secret' => $this->clientSecret, + 'grant_type' => 'refresh_token', + 'refresh_token' => $refreshToken + )) + ->send($url); + + $vaild = $this->checkResponse(); + + if ($vaild) { + $this->tokenData = $this->request->getResponse(); + $this->tokenData['expires_at'] = $this->tokenData['expires_in'] + time(); - $this->tokenData = $response; - $this->tokenData['expires_at'] = $this->tokenData['expires_in'] + time(); + $this->trigger('new_access_token', $this->tokenData); - return $this->tokenData; + return $this->tokenData; + } + + return false; } /** @@ -243,7 +327,7 @@ public function requestTokenWithRefreshToken($refreshToken) public function authenticate() { $response = true; - + if (!$this->isAccessTokenValid()) { if (!$this->hasRefreshToken()) { // no tokens available, so we need to request new ones @@ -261,25 +345,36 @@ public function authenticate() } elseif ($this->hasRefreshToken()) { // use the refresh token to get a new access token - $response = $this->requestTokenWithRefreshToken($this->getRefreshToken()); - - //TODO if the refresh token is expired we need to automatically request a new access token + try { + $response = $this->requestTokenWithRefreshToken($this->getRefreshToken()); + } + catch (\Exception $e) { + print_r($e->getmessage()); + switch ($e->getMessage()) { + case Api::EXPIRED_REFRESH_TOKEN: + $this->trigger('refresh_token_expired'); + // Refresh token is expired so fallback to another method + $this->clearAllTokens(); + $this->authenticate(); + break; + } + + // just throw the original Exception for any other errors + throw $e; + } } } - - //TODO check for response errors here - return $response; } private function getAuthUrl() { - return TrackviaApi::BASE_URL . self::AUTH_URL; + return Api::BASE_URL . self::AUTH_URL; } private function getTokenUrl() { - return TrackviaApi::BASE_URL . self::TOKEN_URL; + return Api::BASE_URL . self::TOKEN_URL; } } \ No newline at end of file diff --git a/Trackvia/EventDispatcher.php b/Trackvia/EventDispatcher.php index 5d9f10d..b9471de 100644 --- a/Trackvia/EventDispatcher.php +++ b/Trackvia/EventDispatcher.php @@ -6,9 +6,10 @@ * * Bind functions to an object by event name. * Then trigger events from that object which then executes binded functions. + * * @author otake */ -abstract class EventDispatcher +abstract class EventDispatcher { /** * Array to store callbacks for different event names @@ -21,18 +22,18 @@ abstract class EventDispatcher * @param string $event * @param string|array $callback A callback function name that can be passed into call_user_func() */ - public function on($event, $callback) + public function on($event, $callback, $extraParams = array()) { + if (!is_callable($callback)) { + throw new \Exception("Callback argument is not callable. Check you have the right function name."); + } + if (!isset($this->events[$event])) { // initialize an array for this event name $this->events[$event] = array(); } - if (!is_callable($callback)) { - throw new \Exception("Callback argument is not callable. Check you have the right function name."); - } - - $this->events[$event][] = $callback; + $this->events[$event][] = array($callback, $extraParams); } /** @@ -45,8 +46,8 @@ public function trigger($event, $data = array()) { if (isset($this->events[$event])) { // loop through each callback for this event - foreach ($this->events[$event] as $callback) { - call_user_func($callback, $data); + foreach ($this->events[$event] as $callbackData) { + call_user_func($callbackData[0], $data, $callbackData[1]); } } } diff --git a/Trackvia/Log.php b/Trackvia/Log.php old mode 100644 new mode 100755 index b96719c..3552645 --- a/Trackvia/Log.php +++ b/Trackvia/Log.php @@ -1,32 +1,110 @@ log = new Logger('name'); - $this->log->pushHandler(new StreamHandler('/Users/coake/api.log', Logger::WARNING)); - - $this->initListeners(); - } - - private function initListeners() - { - $subject->on('api_request', array($this, 'onApiRequest')); - } - - public function onApiRequest($data) - { - var_dump($data); - $this->log->addInfo("API Request made to url $url"); - } + private $log; + private $subject; + + public function __construct($subject) + { + $this->subject = $subject; + + // pass in the events we want to listen to + $this->initListeners(array( + // Api class + // 'authenticate', + 'new_access_token', + 'api_request_init', + 'api_request_send', + 'api_request_complete', + + //Authentication class + 'is_token_valid', + 'has_refresh_token', + 'request_token_with_user_creds', + 'request_token_with_refresh_token', + 'refresh_token_expired' + )); + } + + private function logInfo($text) + { + echo "INFO :: " . get_class($this->subject) . " :: $text\n"; + } + + /** + * Setup the event listeners so we can log certain events. + * Events name are auto mapped to method name on_$eventname + * @param array $events + */ + private function initListeners($events) + { + foreach ($events as $event) { + $this->subject->on($event, array($this, 'on_' . $event)); + } + } + +// Api event listeners + public function on_authenticate($data) + { + $this->logInfo("Authenticating with Trackvia server"); + } + + public function on_new_access_token($data) + { + $this->logInfo('New access token granted'); + print_r($data); + } + + public function on_api_request_init($data) + { + $url = $data['url']; + $this->logInfo("Init api request to url \"$url\""); + } + + public function on_api_request_send($data) + { + $url = $data['url']; + $method = $data['http_method']; + $this->logInfo("Sending $method request to url \"$url\""); + } + + public function on_api_request_complete($data) + { + $url = $data['url']; + $this->logInfo("Request Complete"); + echo "API Response Data:\n"; + print_r($data['response']); + } + + +// Authentication event listeners + public function on_is_token_valid($data) + { + $this->logInfo("Is current access token valid? " . ($data['is_valid'] ? 'Yes' : 'No') ); + } + + public function on_has_refresh_token($data) + { + $this->logInfo("Is there a refresh token? " . ($data['refresh_token'] ? 'Yes' : 'No') ); + } + + public function on_request_token_with_user_creds($data) + { + $username = $data['username']; + $password = $data['password']; + $this->logInfo("Requesting new access token with user creds - user: $username, pass: $password"); + } + + public function on_request_token_with_refresh_token($data) + { + $refresh_token = $data['refresh_token']; + $this->logInfo("Requesting new access token with refresh token - token: $refresh_token"); + } + + public function on_refresh_token_expired() + { + $this->logInfo("Refresh token is expired"); + } } \ No newline at end of file diff --git a/Trackvia/Request.php b/Trackvia/Request.php old mode 100644 new mode 100755 index c54a5dc..d1f9a38 --- a/Trackvia/Request.php +++ b/Trackvia/Request.php @@ -3,126 +3,115 @@ class Request { - private $curl; - - private $accessToken; - - private $isTokenExpired = false; - - public function __construct() - { - $this->curl = curl_init(); - } - - public function setReturnType($type) - { - # code... - } - - public function request($url, $httpMethod = 'POST', $data = null) - { - if ( !in_array($httpMethod, array('POST', 'GET', 'PUT', 'DELETE')) ) { - throw new Exception('Request type "' . $httpMethod . '" not supported'); - } - $ch = $this->curl; - - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $httpMethod); - if (is_array($data) && count($data) > 0) { - // set any post data - curl_setopt($ch, CURLOPT_POSTFIELDS, $data); - } - - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); + private $curl; + + /** + * The data returned from the last response + * @var mixed + */ + private $response; + + private $method = 'GET'; + + private $url; + + private $postData; + + private $headers = array(); + + public function __construct() + { + $this->curl = curl_init(); + } + + public function addHeader($name, $value) + { + $this->headers[$name] = $value; + return $this; + } + + public function setContentType($contentType) + { + return $this->addHeader('Content-Type', $contentType); + } + + public function setMethod($method) + { + $method = strtoupper($method); + if ( !in_array($method, array('POST', 'GET', 'PUT', 'DELETE')) ) { + throw new \Exception('HTTP request method "' . $method . '" not supported'); + } + $this->method = $method; + return $this; + } + + public function setData($data) + { + $this->postData = $data; + return $this; + } + + public function send($url) + { + $data = $this->postData; + + // tack data onto the query string for GET requests + if ($this->method == 'GET' && is_array($data) && !empty($data)) { + + $queryString = http_build_query($data); + if (strpos($url, '?') === false) { + $url .= '?'.$queryString; + } else { + // query string is already part of the url + $url .= '&'.$queryString; + } + $data = array(); //empty the data array + } + + $ch = $this->curl; + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->method); + if (!empty($data)) { + // set any post data + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + } + + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_HEADER, 0); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); - - $response = curl_exec($ch); - $response = json_decode($response, true); - - if (isset($response['error'])) { - $this->parseError($reponse); - return false; - } - return $response; - } - - /** - * GET request. - * Additional data will be appended to the URL. - * - * @param string $url - * @param array $data Data to add on to the request - * @return array - */ - public function get($url, $data) - { - $url = $url . '?' . http_build_query($data); - return $this->request($url, 'GET'); - } - - /** - * POST request - * - * @param string $url - * @param aray $data POST fields to send with request - * @return array - */ - public function post($url, $data) - { - return $this->request($url, 'POST', $data); - } - - /** - * PUT request - * - * @param string $url - * @param aray $data Data fields to send with request - * @return array - */ - public function put($url, $data) - { - return $this->request($url, 'PUT', $data); - } - - /** - * DELETE request - * - * @param string $url - * @param aray $data Data fields to send with request - * @return array - */ - public function delete($url, $data) - { - return $this->request($url, 'DELETE', $data); - } - - private function parseError($data) - { - switch ($data['error_description']) { - case self::ERROR_EXPIRED_TOKEN: - $this->isTokenExpired = true; - // return here so we don't throw this error - // so we can use the refresh token - return; - } - - // throw an exception with the returned error message - throw new Exception($data['error_description']); - } - - /** - * Repeat the last - * @return [type] [description] - */ - public function repeatRequest() - { - - } - - public function isTokenExpired() - { - return $this->isTokenExpired; - } + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); + + // set any headers + if (!empty($this->headers)) { + $headers = array(); + foreach ($this->headers as $name => $value) { + $headers[] = "$name: $value"; + } + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + } + + $this->response = curl_exec($ch); + $this->response = json_decode($this->response, true); + + return $this->response; + } + + /** + * Get the data returned from the last response + * @return mixed + */ + public function getResponse() + { + return $this->response; + } + + /** + * Get the response http code from the last request + * @return int + */ + public function getResponseCode() + { + return (int) curl_getinfo($this->curl, CURLINFO_HTTP_CODE); + } + } \ No newline at end of file diff --git a/api_sample.php b/api_sample.php deleted file mode 100644 index 52aa57d..0000000 --- a/api_sample.php +++ /dev/null @@ -1,93 +0,0 @@ - 'ZDA4ZTAzZWNhODI1YzViNGU0YjUwOWU2ZWUxMTljYzUxYzc0YTExMjViNTYyYWYwMDcxYTliZGM1ZTBiMjJhMANULL', - 'refresh_token' => '', - 'expires_at' => '' - ); -} - -$tokenData = load_saved_token_data(); -$extraParams = array('this can be whatever you want'); - -// Create a TrackviaApi object with your clientId and secret. -// The client_id and secret are only user when you need to request a new access token. -$tv = new Api(array( - 'client_id' => $clientId, - 'client_secret' => $clientSecret, - 'username' => $username, - 'password' => $password -)); - -// setup the logger for debugging -$log = new Log($tv); - -// attach a listener function for when a new token is generated so you can save it to a database -$tv->on('new_token', 'save_token_data', $extraParams); - -// If there is saved token data to use, set it now -if (!empty($savedToken) && isset($savedToken['access_token'])) { - $tv->setTokenData(array( - 'access_token' => $savedToken['access_token'], - 'refresh_token' => $savedToken['refresh_token'], - 'expires_at' => $savedToken['expires_at'] - )); -} - -if (!$tokenData) { - // request a new access token with user credentials you already passed in - // this will trigger the "new_token" listener you created above so you can save the token data - $tv->authenticate(); - -} else { - // set the current token data you have - // the library will auto check the expiresAt so we don't waste a request - // If it is expired, a new access token - $tv->setTokenData(array( - 'access_token' => $tokenData['access_token'], - 'refresh_token' => $tokenData['refresh_token'], - 'expires_at' => $tokenData['expires_at'] - )); -} - - -$appId = 1; - -if ($apps = $tv->getApps()) { - // do something with apps list - // var_dump($apps); exit; -} else { - // request failed - // did access token expire? - -} - - -var_dump($apps); exit; \ No newline at end of file diff --git a/example.php b/example.php new file mode 100755 index 0000000..ad74505 --- /dev/null +++ b/example.php @@ -0,0 +1,210 @@ + 'zH_4mT6c71qn8fD5_zIKabURN7jBj0oKvz19OUvVb5I', + // 'access_token' => 'IR5LenHFiNGxn5ogNNMpqb28pwC-PiPLjlO8kwm0OYc', + 'refresh_token' => 'v5G2ZFOIx3YM0bpggN1MP_WwhVleo4zvpY2A8kEzq30', + // 'expires_at' => '', + 'expires_at' => '1344359784' + ); +} +$savedToken = load_saved_token_data(); + +// extra parameters to pass in for the "new_token" event callback +$extraParams = array('extra_params' => 'this can be whatever you want'); + + +// Create a TrackviaApi object with your clientId and secret. +// The client_id and secret are only user when you need to request a new access token. +$tv = new Api(array( + 'client_id' => $clientId, + 'client_secret' => $clientSecret, + 'username' => $username, + 'password' => $password +)); + +// setup the logger for debugging +$authLog = new Log($tv->getAuthentication()); +$log = new Log($tv); + +// attach a listener function for when a new token is generated so you can save it to a database +$tv->on('new_token', function ($tokenData, $extraParams) { + $accessToken = $tokenData['access_token']; + $refreshToken = $tokenData['refresh_token']; + + // timestamp when the access token expires + $expiresAt = $tokenData['expires_at']; + + //============ + // code to save token data to your database + //============ + +}, $extraParams); + +// If there is saved token data to use, set it now +if (!empty($savedToken) && isset($savedToken['access_token'])) { + + $tv->setTokenData(array( + 'access_token' => $savedToken['access_token'], + 'refresh_token' => $savedToken['refresh_token'], + 'expires_at' => $savedToken['expires_at'] + )); + +} + +/* + +//********************* +// Get a list of all apps accessible by the user +//********************* +$apps = $tv->getApps(); + + +//********************* +// Get app data for a specific app_id +//********************* +$app = $tv->getApp(22087); + + +//********************* +// Get data for a table +//********************* +$table = $tv->getTable(62995); + + +//********************* +// Get data for a view +//********************* +$view = $tv->getView(289592); + + +//************** +// Get a record +//************** +$record = $tv->getRecord(77113576); + + +//****************** +// Add a new record +//****************** +$tableId = 62994; +$record = array( + 'Times Visited' => 1, + 'City' => 'Denver', + 'State' => 'CO', + 'Menu Link' => 'http://www.google.com', + 'Restaurant' => 'Fake Restaurant 1', + 'Address' => '1555 Blake Street', + 'Hours' => '11am - 11pm', + 'Phone Number' => '720.524.4345', + 'Cuisine' => 'Burgers', + 'Notes' => 'Order by phone. No delivery.', + 'Zip' => '80202' +); +$tv->addRecord($tableId, $record); + + +//********************** +// Add multiple records +// (batch inserts) +//********************** +$tableId = 62994; +$records = array(); +$records[] = array( + 'table_id' => $tableId, + 'data' => array( + 'Times Visited' => 1, + 'City' => 'Denver', + 'State' => 'CO', + 'Restaurant' => 'Fake Restaurant 2' + 'Address' => '1555 Blake Street', + 'Phone Number' => '720.524.4345', + 'Cuisine' => 'Burgers', + 'Notes' => 'Order by phone. No delivery.', + 'Zip' => '80202' + ) +); +$records[] = array( + 'table_id' => $tableId, + 'data' => array( + 'Times Visited' => 3, + 'City' => 'Denver', + 'State' => 'CO', + 'Restaurant' => 'Fake Restaurant 3' + 'Address' => '1555 Blake Street', + 'Phone Number' => '720.524.4345', + 'Cuisine' => 'Burgers', + 'Notes' => 'Order by phone. No delivery.', + 'Zip' => '80202' + ) +); +$tv->addRecords($records); + + +//************************ +// Update a single record +//************************ +$recordId = 105973377; +$record = array( + 'Times Visited' => 5, + 'Restaurant' => 'Fake Restaurant Updated!!' +); +$tv->updateRecord($recordId, $record); + + +//************************ +// Update multiple records +// (batch updates) +//************************ +$records = array(); +// 1st record +$records[] = array( + 'id' => 105973376, // record id + 'data' => array( + 'Times Visited' => 12, + 'Restaurant' => 'Fake Restaurant Updated2!!' + ) +); +// 2nd record +$records[] = array( + 'id' => 105973377, // record id + 'data' => array( + 'Times Visited' => 13, + 'Restaurant' => 'Fake Restaurant Updated3!!' + ) +); +$tv->updateRecords($records); + + +//************************ +// Delete a single record +//************************ +$tv->deleteRecord(105973364); + + +//************************ +// Delete a multiple records +// (batch delete) +//************************ +$tv->deleteRecords(array(105973376, 105973377)); +*/ \ No newline at end of file From fd14481d65b6707bc6390e86f728ecd582daa244 Mon Sep 17 00:00:00 2001 From: Chris Oake Date: Mon, 6 Aug 2012 18:04:37 -0600 Subject: [PATCH 04/14] added readme --- README | 1 + 1 file changed, 1 insertion(+) create mode 100644 README diff --git a/README b/README new file mode 100644 index 0000000..e268495 --- /dev/null +++ b/README @@ -0,0 +1 @@ +PHP Library for Trackvia API \ No newline at end of file From ebb54780491830a9dea5a2753fad5c2b1a5fe068 Mon Sep 17 00:00:00 2001 From: Chris Oake Date: Mon, 6 Aug 2012 18:15:04 -0600 Subject: [PATCH 05/14] changed creds --- example.php | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/example.php b/example.php index ad74505..55a2882 100755 --- a/example.php +++ b/example.php @@ -6,12 +6,12 @@ use Trackvia\Log; // Set your API client credentials -$clientId = '13_2s6wg16cwtk48kcgggo8kcgow44w0k8k4800ssw4oss0coc0g8'; -$clientSecret = '4htbckzh1qm8wo4s88gw44g8gs80g00so0sg0kw8kkoccco8gg'; +$clientId = 'your_client_key'; +$clientSecret = 'your_client_secrect'; // username and password to request an Access Token -$username = 'api.tester'; -$password = 'co3823se'; +$username = 'testuser'; +$password = 'testpass'; // load the saved token for this user function load_saved_token_data() @@ -20,13 +20,11 @@ function load_saved_token_data() // code to load the token from your database //=========== - return array( - 'access_token' => 'zH_4mT6c71qn8fD5_zIKabURN7jBj0oKvz19OUvVb5I', - // 'access_token' => 'IR5LenHFiNGxn5ogNNMpqb28pwC-PiPLjlO8kwm0OYc', - 'refresh_token' => 'v5G2ZFOIx3YM0bpggN1MP_WwhVleo4zvpY2A8kEzq30', - // 'expires_at' => '', - 'expires_at' => '1344359784' - ); + // return array( + // 'access_token' => 'user_access_token', + // 'refresh_token' => 'user_refresh_token', + // 'expires_at' => 'expires_timestamp' + // ); } $savedToken = load_saved_token_data(); @@ -43,10 +41,6 @@ function load_saved_token_data() 'password' => $password )); -// setup the logger for debugging -$authLog = new Log($tv->getAuthentication()); -$log = new Log($tv); - // attach a listener function for when a new token is generated so you can save it to a database $tv->on('new_token', function ($tokenData, $extraParams) { $accessToken = $tokenData['access_token']; @@ -72,6 +66,10 @@ function load_saved_token_data() } +// setup the logger for debugging +// $authLog = new Log($tv->getAuthentication()); +// $log = new Log($tv); + /* //********************* From 79e564aa6add087a4dd3573a75331ab5ab8bab79 Mon Sep 17 00:00:00 2001 From: Chris Oake Date: Mon, 6 Aug 2012 18:20:08 -0600 Subject: [PATCH 06/14] fucking tabs --- Trackvia/Authentication.php | 98 ++++++++++++++++++------------------- example.php | 2 +- 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/Trackvia/Authentication.php b/Trackvia/Authentication.php index 46e6a06..c11e184 100755 --- a/Trackvia/Authentication.php +++ b/Trackvia/Authentication.php @@ -3,23 +3,23 @@ class Authentication extends EventDispatcher { - /** - * Path to the OAuth2 authentication endpoint - */ - const AUTH_TOKEN = 'oauth/v2/auth'; + /** + * Path to the OAuth2 authentication endpoint + */ + const AUTH_TOKEN = 'oauth/v2/auth'; /** * Path to the token endpoint */ const TOKEN_URL = 'oauth/v2/token'; - /** - * Object to handle http requests - * @var TrackviaRequest - */ - private $request; + /** + * Object to handle http requests + * @var TrackviaRequest + */ + private $request; - /** + /** * Client Id passed in by the client app * @var string */ @@ -37,25 +37,25 @@ class Authentication extends EventDispatcher */ private $userCreds; - /** - * Array containing any token data returned after user authentication - * @var array - */ - private $tokenData; + /** + * Array containing any token data returned after user authentication + * @var array + */ + private $tokenData; - private $isTokenExpired = false; + private $isTokenExpired = false; private $lastError; - /** - * @param TrackviaRequst $request - */ - public function __construct(Request $request, $params) - { - $this->request = $request; + /** + * @param TrackviaRequst $request + */ + public function __construct(Request $request, $params) + { + $this->request = $request; - if (!is_array($params)) { + if (!is_array($params)) { throw new \Exception('You must pass in your client_id and client_secrect'); } if (!isset($params['client_id'])) { @@ -72,9 +72,9 @@ public function __construct(Request $request, $params) // user credentials flow is being used $this->setUserCreds($params['username'], $params['password']); } - } + } - /** + /** * Set the user credentials to use for authentication * @param string $username * @param string $password @@ -115,7 +115,7 @@ public function setTokenData($params) */ public function getTokenData() { - return $this->tokenData; + return $this->tokenData; } /** @@ -168,7 +168,7 @@ public function getExpiresAt() */ public function clearAccessToken() { - if ($this->hasAccessToken()) { + if ($this->hasAccessToken()) { $this->tokenData['access_token'] = null; } } @@ -204,7 +204,7 @@ private function isAccessTokenValid() { $retval = $this->hasAccessToken() && !$this->isAccessTokenExpired(); $this->trigger('is_token_valid', array('is_valid' => $retval)); - return $retval; + return $retval; } /** @@ -236,7 +236,7 @@ private function checkResponse() return true; } - /** + /** * Get an access token from the Trackvia OAuth2 server. * * @param string $username The user's username credential @@ -326,25 +326,25 @@ public function requestTokenWithRefreshToken($refreshToken) */ public function authenticate() { - $response = true; + $response = true; - if (!$this->isAccessTokenValid()) { - if (!$this->hasRefreshToken()) { - // no tokens available, so we need to request new ones - - // check for user credentials flow first - if ($this->hasUserCreds()) { - $response = $this->requestTokenWithUserCreds( - $this->userCreds['username'], - $this->userCreds['password'] - ); - } - - //TODO add support for redirecting user to auth trackvia endpoint - - } - elseif ($this->hasRefreshToken()) { - // use the refresh token to get a new access token + if (!$this->isAccessTokenValid()) { + if (!$this->hasRefreshToken()) { + // no tokens available, so we need to request new ones + + // check for user credentials flow first + if ($this->hasUserCreds()) { + $response = $this->requestTokenWithUserCreds( + $this->userCreds['username'], + $this->userCreds['password'] + ); + } + + //TODO add support for redirecting user to auth trackvia endpoint + + } + elseif ($this->hasRefreshToken()) { + // use the refresh token to get a new access token try { $response = $this->requestTokenWithRefreshToken($this->getRefreshToken()); } @@ -362,8 +362,8 @@ public function authenticate() // just throw the original Exception for any other errors throw $e; } - } - } + } + } return $response; } diff --git a/example.php b/example.php index 55a2882..3d846cd 100755 --- a/example.php +++ b/example.php @@ -33,7 +33,7 @@ function load_saved_token_data() // Create a TrackviaApi object with your clientId and secret. -// The client_id and secret are only user when you need to request a new access token. +// The client_id and secret are only used when you need to request a new access token. $tv = new Api(array( 'client_id' => $clientId, 'client_secret' => $clientSecret, From ab7392c0c80e0c03b81b49d1ffb0fc89b2f1b525 Mon Sep 17 00:00:00 2001 From: Chris Oake Date: Tue, 7 Aug 2012 15:20:02 -0600 Subject: [PATCH 07/14] Added some Authentication unit test --- Tests/ApiTest.php | 22 ++++++ Tests/AuthenticationTest.php | 126 +++++++++++++++++++++++++++++++++++ Trackvia/Api.php | 15 +++-- Trackvia/Authentication.php | 53 ++++++++------- Trackvia/Request.php | 1 + example.php | 63 +++++++++--------- 6 files changed, 220 insertions(+), 60 deletions(-) create mode 100644 Tests/ApiTest.php create mode 100644 Tests/AuthenticationTest.php diff --git a/Tests/ApiTest.php b/Tests/ApiTest.php new file mode 100644 index 0000000..abb68f2 --- /dev/null +++ b/Tests/ApiTest.php @@ -0,0 +1,22 @@ + '13_2s6wg16cwtk48kcgggo8kcgow44w0k8k4800ssw4oss0coc0g8', + 'client_secret' => '4htbckzh1qm8wo4s88gw44g8gs80g00so0sg0kw8kkoccco8gg' + ); + $this->api = new Trackvia\Api($params); + } + + public function testAuthenticate() + { + $this->assertNotEmpty($this->api); + } +} \ No newline at end of file diff --git a/Tests/AuthenticationTest.php b/Tests/AuthenticationTest.php new file mode 100644 index 0000000..33b929c --- /dev/null +++ b/Tests/AuthenticationTest.php @@ -0,0 +1,126 @@ + '13_2s6wg16cwtk48kcgggo8kcgow44w0k8k4800ssw4oss0coc0g8', + 'client_secret' => '4htbckzh1qm8wo4s88gw44g8gs80g00so0sg0kw8kkoccco8gg' + ); + $this->request = new Trackvia\Request(); + $this->auth = new Trackvia\Authentication($this->request, $params); + + $this->setMockTokenData(); + } + + protected function setMockTokenData($expiresAt = null) + { + $this->auth->setTokenData(array( + 'access_token' => 'zH_4mT6c71qn8fD5_zIKabURN7jBj0oKvz19OUvVb5I', + 'refresh_token' => 'v5G2ZFOIx3YM0bpggN1MP_WwhVleo4zvpY2A8kEzq30', + 'expires_at' => ($expiresAt != null ? $expiresAt : (time() + 10000)) + )); + } + + public function testSetUserCreds() + { + $this->assertFalse($this->auth->hasUserCreds()); + + $this->auth->setUserCreds('testuser', 'testpassword'); + + $this->assertTrue($this->auth->hasUserCreds()); + } + + public function testClearAccessToken() + { + $this->assertTrue($this->auth->hasAccessToken()); + + $this->auth->clearAccessToken(); + + $this->assertFalse($this->auth->hasAccessToken()); + } + + public function testClearAllTokens() + { + $auth = $this->auth; + $this->assertNotEmpty($auth->getTokenData()); + + $auth->clearAllTokens(); + + $this->assertEmpty($auth->getTokenData()); + } + + public function testSetTokenData() + { + $auth = $this->auth; + $auth->clearAllTokens(); + + // verify there is no token data first + $this->assertEmpty($auth->getTokenData()); + $this->assertEmpty($auth->getAccessToken()); + $this->assertFalse($auth->hasAccessToken()); + $this->assertFalse($auth->hasRefreshToken()); + $this->assertEmpty($auth->getRefreshToken()); + + $this->setMockTokenData(); + + $this->assertNotEmpty($auth->getTokenData()); + $this->assertNotEmpty($auth->getAccessToken()); + $this->assertTrue($auth->hasAccessToken()); + $this->assertTrue($auth->hasRefreshToken()); + $this->assertNotEmpty($auth->getRefreshToken()); + } + + public function testIsAccessTokenExpired() + { + // expiresAt should already be set ahead from now + $this->assertFalse($this->auth->isAccessTokenExpired()); + $this->setMockTokenData(time() - 10000); + $this->assertTrue($this->auth->isAccessTokenExpired()); + } + + public function testAuthenticateWithExistingToken() + { + $this->assertTrue($this->auth->authenticate()); + } + + public function testAuthenticateWithUserCreds() + { + $auth = $this->auth; + $this->auth->clearAllTokens(); + + // auth should fail with no token or user creds + $this->assertFalse($auth->authenticate()); + $this->assertEmpty($auth->getTokenData()); + + $this->auth->setUserCreds('api.tester', 'co3823se'); + $response = $this->auth->authenticate(); + + // make sure we have some token data now + $this->assertNotEmpty($auth->getTokenData()); + + return $response; + } + + public function testAuthenticateWithBadUserCreds($value='') + { + # code... + } + + /** + * @depends testAuthenticateWithUserCreds + */ + public function testAuthenticateWithRefreshToken($tokenData) + { + $this->assertNotEmpty($tokenData['refresh_token']); + $this->auth->setTokenData($tokenData); + $this->auth->clearAccessToken(); + + $response = $this->auth->authenticate(); + $this->assertTrue($this->auth->hasAccessToken()); + } +} \ No newline at end of file diff --git a/Trackvia/Api.php b/Trackvia/Api.php index 602054d..e61f270 100755 --- a/Trackvia/Api.php +++ b/Trackvia/Api.php @@ -1,9 +1,9 @@ auth->setTokenData($tokenData); } + public function setUserCredentials($username, $password) + { + $this->auth->setUserCreds($username, $password); + } + public function getAuthentication() { return $this->auth; diff --git a/Trackvia/Authentication.php b/Trackvia/Authentication.php index c11e184..7f7b7e7 100755 --- a/Trackvia/Authentication.php +++ b/Trackvia/Authentication.php @@ -91,7 +91,7 @@ public function setUserCreds($username, $password) * Whether or not user creds are provided * @return boolean */ - private function hasUserCreds() + public function hasUserCreds() { return ( !empty($this->userCreds) && @@ -133,7 +133,7 @@ public function hasAccessToken() */ public function getAccessToken() { - return $this->tokenData['access_token']; + return isset($this->tokenData['access_token']) ? $this->tokenData['access_token'] : null; } /** @@ -142,7 +142,17 @@ public function getAccessToken() */ public function hasRefreshToken() { - return !empty($this->tokenData) && isset($this->tokenData['refresh_token']) && $this->tokenData['refresh_token'] != ''; + if( !empty($this->tokenData) && isset($this->tokenData['refresh_token']) && $this->tokenData['refresh_token']) { + $retval = true; + $token = $this->tokenData['refresh_token']; + } else { + $retval = false; + $token = null; + } + + $this->trigger('has_refresh_token', array('refresh_token' => $token)); + + return $retval; } /** @@ -151,9 +161,7 @@ public function hasRefreshToken() */ public function getRefreshToken() { - $token = $this->tokenData['refresh_token']; - $this->trigger('has_refresh_token', array('refresh_token' => $token)); - return $token; + return isset($this->tokenData['refresh_token']) ? $this->tokenData['refresh_token'] : null; } public function getExpiresAt() @@ -186,7 +194,7 @@ public function clearAllTokens() * We check the expired_at time that should be set by the client. * @return boolean */ - private function isAccessTokenExpired() + public function isAccessTokenExpired() { if (!isset($this->tokenData['expires_at']) || $this->tokenData['expires_at'] <= time()) { return true; @@ -200,7 +208,7 @@ private function isAccessTokenExpired() * * @return boolean */ - private function isAccessTokenValid() + public function isAccessTokenValid() { $retval = $this->hasAccessToken() && !$this->isAccessTokenExpired(); $this->trigger('is_token_valid', array('is_valid' => $retval)); @@ -226,9 +234,10 @@ private function checkResponse() return false; } - if ($httpCode == 400 && isset($response['error_description'])) { + if ($httpCode == 400 && isset($response['error'])) { // throw an Exception with the returned error message - throw new \Exception($response['error_description']); + $msg = isset($response['error_description']) ? $response['error_description'] : $response['error']; + throw new \Exception($msg); return false; } @@ -254,7 +263,6 @@ public function requestTokenWithUserCreds($username, $password) $this->request ->setMethod('post') - ->setContentType('application/x-www-form-urlencoded') ->setData(array( 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, @@ -264,9 +272,9 @@ public function requestTokenWithUserCreds($username, $password) )) ->send($url); - $vaild = $this->checkResponse(); + $valid = $this->checkResponse(); - if ($vaild) { + if ($valid) { $this->tokenData = $this->request->getResponse(); $this->tokenData['expires_at'] = $this->tokenData['expires_in'] + time(); @@ -295,7 +303,6 @@ public function requestTokenWithRefreshToken($refreshToken) $this->request ->setMethod('post') - ->setContentType('application/x-www-form-urlencoded') ->setData(array( 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, @@ -304,9 +311,9 @@ public function requestTokenWithRefreshToken($refreshToken) )) ->send($url); - $vaild = $this->checkResponse(); + $valid = $this->checkResponse(); - if ($vaild) { + if ($valid) { $this->tokenData = $this->request->getResponse(); $this->tokenData['expires_at'] = $this->tokenData['expires_in'] + time(); @@ -326,9 +333,11 @@ public function requestTokenWithRefreshToken($refreshToken) */ public function authenticate() { - $response = true; + $response = false; - if (!$this->isAccessTokenValid()) { + if ($this->isAccessTokenValid()) { + $response = true; + } else { if (!$this->hasRefreshToken()) { // no tokens available, so we need to request new ones @@ -341,15 +350,13 @@ public function authenticate() } //TODO add support for redirecting user to auth trackvia endpoint - } - elseif ($this->hasRefreshToken()) { + else { // use the refresh token to get a new access token try { $response = $this->requestTokenWithRefreshToken($this->getRefreshToken()); } catch (\Exception $e) { - print_r($e->getmessage()); switch ($e->getMessage()) { case Api::EXPIRED_REFRESH_TOKEN: $this->trigger('refresh_token_expired'); @@ -368,12 +375,12 @@ public function authenticate() return $response; } - private function getAuthUrl() + public function getAuthUrl() { return Api::BASE_URL . self::AUTH_URL; } - private function getTokenUrl() + public function getTokenUrl() { return Api::BASE_URL . self::TOKEN_URL; } diff --git a/Trackvia/Request.php b/Trackvia/Request.php index d1f9a38..a3fbc07 100755 --- a/Trackvia/Request.php +++ b/Trackvia/Request.php @@ -87,6 +87,7 @@ public function send($url) foreach ($this->headers as $name => $value) { $headers[] = "$name: $value"; } + print_r($headers); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); } diff --git a/example.php b/example.php index 3d846cd..69be126 100755 --- a/example.php +++ b/example.php @@ -5,13 +5,16 @@ use Trackvia\Api; use Trackvia\Log; -// Set your API client credentials -$clientId = 'your_client_key'; -$clientSecret = 'your_client_secrect'; - -// username and password to request an Access Token -$username = 'testuser'; -$password = 'testpass'; +// Create a TrackviaApi object with your clientId and secret. +// The client_id and secret are only used when you need to request a new access token. +$tv = new Api(array( + 'client_id' => 'your_client_id', + 'client_secret' => 'your_client_secret' + + // You can optionally pass in the user credentials here too + // 'username' => 'sample_username', + // 'password' => 'sample_password' +)); // load the saved token for this user function load_saved_token_data() @@ -28,18 +31,23 @@ function load_saved_token_data() } $savedToken = load_saved_token_data(); -// extra parameters to pass in for the "new_token" event callback -$extraParams = array('extra_params' => 'this can be whatever you want'); +// If there is saved token data to use, set it now +if (!empty($savedToken) && isset($savedToken['access_token'])) { + $tv->setTokenData(array( + 'access_token' => $savedToken['access_token'], + 'refresh_token' => $savedToken['refresh_token'], + 'expires_at' => $savedToken['expires_at'] + )); -// Create a TrackviaApi object with your clientId and secret. -// The client_id and secret are only used when you need to request a new access token. -$tv = new Api(array( - 'client_id' => $clientId, - 'client_secret' => $clientSecret, - 'username' => $username, - 'password' => $password -)); +} else { + // No token data. + // So you need to get the user credentials for authentication. + $tv->setUserCreds('sample_username', 'sample_password'); +} + +// extra parameters to pass in for the "new_token" event callback +$extraParams = array('extra_params' => 'this can be whatever you want'); // attach a listener function for when a new token is generated so you can save it to a database $tv->on('new_token', function ($tokenData, $extraParams) { @@ -55,23 +63,14 @@ function load_saved_token_data() }, $extraParams); -// If there is saved token data to use, set it now -if (!empty($savedToken) && isset($savedToken['access_token'])) { - - $tv->setTokenData(array( - 'access_token' => $savedToken['access_token'], - 'refresh_token' => $savedToken['refresh_token'], - 'expires_at' => $savedToken['expires_at'] - )); - -} - -// setup the logger for debugging -// $authLog = new Log($tv->getAuthentication()); -// $log = new Log($tv); - /* +//********************* +// Setup the logger for debugging (optional) +//********************* +$authLog = new Log($tv->getAuthentication()); +$log = new Log($tv); + //********************* // Get a list of all apps accessible by the user //********************* From 7fedae74c45a261ef6c6f98a8c14a5cf8cf2766d Mon Sep 17 00:00:00 2001 From: Chris Oake Date: Fri, 10 Aug 2012 11:04:16 -0600 Subject: [PATCH 08/14] authentication test for bad creds --- Tests/AuthenticationTest.php | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/Tests/AuthenticationTest.php b/Tests/AuthenticationTest.php index 33b929c..c3dcc64 100644 --- a/Tests/AuthenticationTest.php +++ b/Tests/AuthenticationTest.php @@ -88,13 +88,27 @@ public function testAuthenticateWithExistingToken() $this->assertTrue($this->auth->authenticate()); } + public function testBadCredentialsException() + { + $auth = $this->auth; + $this->auth->clearAllTokens(); + + $this->auth->setUserCreds('api.tester', 'fake_password'); + + try { + $auth->authenticate(); + } catch(\Exception $e) { + $this->assertEquals($e->getMessage(), 'invalid_grant'); + } + } + public function testAuthenticateWithUserCreds() { $auth = $this->auth; $this->auth->clearAllTokens(); // auth should fail with no token or user creds - $this->assertFalse($auth->authenticate()); + $this->assertFalse($auth->authenticate()); $this->assertEmpty($auth->getTokenData()); $this->auth->setUserCreds('api.tester', 'co3823se'); @@ -106,11 +120,6 @@ public function testAuthenticateWithUserCreds() return $response; } - public function testAuthenticateWithBadUserCreds($value='') - { - # code... - } - /** * @depends testAuthenticateWithUserCreds */ From dcf039f65bea557bae7c7e39805279422adec687 Mon Sep 17 00:00:00 2001 From: Chris Oake Date: Wed, 22 Aug 2012 14:15:47 -0600 Subject: [PATCH 09/14] Added docs to README. Added support for forms and dashboards --- README | 1 - README.md | 235 +++++++++++++++++++++++++++++++++++++++++++++++ Trackvia/Api.php | 47 +++++----- example.php | 23 +++++ 4 files changed, 284 insertions(+), 22 deletions(-) delete mode 100644 README create mode 100644 README.md diff --git a/README b/README deleted file mode 100644 index e268495..0000000 --- a/README +++ /dev/null @@ -1 +0,0 @@ -PHP Library for Trackvia API \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..643a109 --- /dev/null +++ b/README.md @@ -0,0 +1,235 @@ +# Trackvia API +## PHP Library + +This library requires that you are at least using PHP 5.3 + +**Code examples are available in example.php** + +The Trackvia API uses OAuth2 for authentication. This PHP library minimizes the work you need to do for OAuth2. +Just provide the proper credentials and token data when needed and the library will handle the authentication for you. + +### Authentication flow (if you're interested): +* Request an access token on behalf of the user. Access tokens are unique per user. You will need the user's credentials to request the first access token. +* You will get back an 'access\_token', 'expires\_at' timestamp, and a 'refresh\_token'. Save this information. +* Now you can make an API request using the access token. +* When the access token expires (in 24 hours), you can request a new one using the refresh token. +* Once the refresh token expires (in 7 days), you go back to the first step of using user credentials to get an access token. + + +# The code +### First, create a new Api object using your client credentials. + + $tv = new Api(array( + 'client_id' => 'your_client_id', + 'client_secret' => 'your_client_secret' + + // You can optionally pass in the user credentials here too + // 'username' => 'sample_username', + // 'password' => 'sample_password' + )); + +Your client ID and secret are specific to your account and are accessible from within your Trackvia account settings. +You must always set the client_id and secret so that the library can automatically request a new token for you. + + +### Add a listener function +It is up to you to save the access token when it is obtained automatically. +The Api class will trigger an event when a new Access Token has been acquired. +You can attach a closure function to this event that handles saving the token data to your database. + + $tv->on('new_token', function ($tokenData, $extraParams) { + $accessToken = $tokenData['access_token']; + $refreshToken = $tokenData['refresh_token']; + + // timestamp when the access token expires + $expiresAt = $tokenData['expires_at']; + + //============ + // code to save token data to your database + //============ + + }, $extraParams); + +The important pieces of data to save are 'access\_token', 'refresh\_token', and 'expires\_at'. + +`$extraParams` is an optional array of values that can be attached with the listener function. +This array will then be passed into the function when it is called. + + +### No access token yet? +If you don't have an access token yet, then you need to set the user's credentials (username, password) before making API requests. + + $tv->setUserCreds('sample_username', 'sample_password'); + + +### Already have an access token? +If you are loading a saved token, set it before making any requests + + $tv->setTokenData(array( + 'access_token' => $savedToken['access_token'], + 'refresh_token' => $savedToken['refresh_token'], + 'expires_at' => $savedToken['expires_at'] + )); + + +### Now make an API request +When a request is successful (200 OK), data will be returned in array format. + +Any errors returned from the API server will be thrown as PHP exceptions. + + + //********************* + // Get a list of dashboards + //********************* + $dashboards = $tv->getDashboards(); + + //********************* + // Get a single dashboard by dashboard_id + //********************* + $dashboards = $tv->getDashboard(2); + + + //********************* + // Get a list of all apps accessible by the user + //********************* + $apps = $tv->getApps(); + + + //********************* + // Get app data for a specific app_id + //********************* + $app = $tv->getApp(22087); + + + //********************* + // Get data for a table + //********************* + $table = $tv->getTable(62995); + + + //********************* + // Get a list of forms for a table + //********************* + $forms = $tv->getForms(62995); + + //********************* + // Get data for a specific form + //********************* + $form = $tv->getForm(3); + + + //********************* + // Get data for a view + //********************* + $view = $tv->getView(289592); + + + //************** + // Get a record + //************** + $record = $tv->getRecord(77113576); + + + //****************** + // Add a new record + //****************** + $tableId = 62994; + $record = array( + 'Times Visited' => 1, + 'City' => 'Denver', + 'State' => 'CO', + 'Menu Link' => 'http://www.google.com', + 'Restaurant' => 'Fake Restaurant 1', + 'Address' => '1555 Blake Street', + 'Hours' => '11am - 11pm', + 'Phone Number' => '720.524.4345', + 'Cuisine' => 'Burgers', + 'Notes' => 'Order by phone. No delivery.', + 'Zip' => '80202' + ); + $tv->addRecord($tableId, $record); + + + //********************** + // Add multiple records + // (batch inserts) + //********************** + $tableId = 62994; + $records = array(); + $records[] = array( + 'table_id' => $tableId, + 'data' => array( + 'Times Visited' => 1, + 'City' => 'Denver', + 'State' => 'CO', + 'Restaurant' => 'Fake Restaurant 2' + 'Address' => '1555 Blake Street', + 'Phone Number' => '720.524.4345', + 'Cuisine' => 'Burgers', + 'Notes' => 'Order by phone. No delivery.', + 'Zip' => '80202' + ) + ); + $records[] = array( + 'table_id' => $tableId, + 'data' => array( + 'Times Visited' => 3, + 'City' => 'Denver', + 'State' => 'CO', + 'Restaurant' => 'Fake Restaurant 3' + 'Address' => '1555 Blake Street', + 'Phone Number' => '720.524.4345', + 'Cuisine' => 'Burgers', + 'Notes' => 'Order by phone. No delivery.', + 'Zip' => '80202' + ) + ); + $tv->addRecords($records); + + + //************************ + // Update a single record + //************************ + $recordId = 105973377; + $record = array( + 'Times Visited' => 5, + 'Restaurant' => 'Fake Restaurant Updated!!' + ); + $tv->updateRecord($recordId, $record); + + + //************************ + // Update multiple records + // (batch updates) + //************************ + $records = array(); + // 1st record + $records[] = array( + 'id' => 105973376, // record id + 'data' => array( + 'Times Visited' => 12, + 'Restaurant' => 'Fake Restaurant Updated2!!' + ) + ); + // 2nd record + $records[] = array( + 'id' => 105973377, // record id + 'data' => array( + 'Times Visited' => 13, + 'Restaurant' => 'Fake Restaurant Updated3!!' + ) + ); + $tv->updateRecords($records); + + + //************************ + // Delete a single record + //************************ + $tv->deleteRecord(105973364); + + + //************************ + // Delete a multiple records + // (batch delete) + //************************ + $tv->deleteRecords(array(105973376, 105973377)); \ No newline at end of file diff --git a/Trackvia/Api.php b/Trackvia/Api.php index e61f270..1bb86be 100755 --- a/Trackvia/Api.php +++ b/Trackvia/Api.php @@ -9,8 +9,9 @@ class Api extends EventDispatcher { const BASE_URL = 'https://api.trackviadev.com/'; - // URLs for API data endpoints + // URLs for API endpoints const DASHBOARDS_URL = 'dashboards'; + const FORMS_URL = 'forms'; const APPS_URL = 'apps'; const TABLES_URL = 'tables'; const VIEWS_URL = 'views'; @@ -201,10 +202,6 @@ private function api($url, $httpMethod = 'GET', $data = array(), $contentType = */ public function getDashboards($id = null) { - if ($id != null && !is_int($id)) { - throw new \Exception('Dashboard ID must be an integer'); - } - // build the url $url = self::BASE_URL . self::DASHBOARDS_URL . ($id ? '/'.$id : ''); @@ -233,10 +230,6 @@ public function getDashboard($id) */ public function getApps($appId = null) { - if ($appId != null && !is_int($appId)) { - throw new \Exception('App ID must be an integer'); - } - // build the url $url = self::BASE_URL . self::APPS_URL . ($appId ? '/'.$appId : ''); @@ -264,10 +257,6 @@ public function getApp($appId) */ public function getTable($tableId) { - if (!is_int($tableId)) { - throw new \Exception('Table ID must be an integer'); - } - // build the url $url = self::BASE_URL . self::TABLES_URL .'/'. $tableId; @@ -283,10 +272,6 @@ public function getTable($tableId) */ public function getView($viewId) { - if (!is_int($viewId)) { - throw new \Exception('View ID must be an integer'); - } - // build the url $url = self::BASE_URL . self::VIEWS_URL .'/'. $viewId; @@ -302,10 +287,6 @@ public function getView($viewId) */ public function getRecord($id) { - if (!is_int($id)) { - throw new \Exception('Record ID must be an integer'); - } - // build the url $url = self::BASE_URL . self::RECORDS_URL .'/'. $id; @@ -389,6 +370,30 @@ public function deleteRecords($data) return $this->api($url, 'DELETE', json_encode($data), 'json'); } + /** + * Get the forms for a table. + * @param int $tableId The id of the table + * @return array + */ + public function getForms($tableId) + { + $url = self::BASE_URL . self::TABLES_URL .'/'. $tableId .'/'. self::FORMS_URL; + + return $this->api($url, 'GET'); + } + + /** + * Get data for a specific form + * @param int $formId The id of the form + * @return array + */ + public function getForm($formId) + { + $url = self::BASE_URL . self::FORMS_URL .'/'. $formId; + + return $this->api($url, 'GET'); + } + /** * Search for records on a table. * diff --git a/example.php b/example.php index 69be126..1a91dac 100755 --- a/example.php +++ b/example.php @@ -71,6 +71,18 @@ function load_saved_token_data() $authLog = new Log($tv->getAuthentication()); $log = new Log($tv); + +//********************* +// Get a list of dashboards +//********************* +$dashboards = $tv->getDashboards(); + +//********************* +// Get a single dashboard by dashboard_id +//********************* +$dashboards = $tv->getDashboard(2); + + //********************* // Get a list of all apps accessible by the user //********************* @@ -89,6 +101,17 @@ function load_saved_token_data() $table = $tv->getTable(62995); +//********************* +// Get a list of forms for a table +//********************* +$forms = $tv->getForms(62995); + +//********************* +// Get data for a specific form +//********************* +$form = $tv->getForm(3); + + //********************* // Get data for a view //********************* From df31aa030a543ef7f98afdd731ab05d7539a0542 Mon Sep 17 00:00:00 2001 From: Chris Oake Date: Wed, 29 Aug 2012 14:48:08 -0600 Subject: [PATCH 10/14] Fixes for latest api batch updates --- README.md | 62 +++++++++---------- Trackvia/Api.php | 50 ++++++++++----- example.php | 156 ----------------------------------------------- 3 files changed, 65 insertions(+), 203 deletions(-) diff --git a/README.md b/README.md index 643a109..0b647f1 100644 --- a/README.md +++ b/README.md @@ -156,35 +156,31 @@ Any errors returned from the API server will be thrown as PHP exceptions. //********************** $tableId = 62994; $records = array(); + // 1st record $records[] = array( - 'table_id' => $tableId, - 'data' => array( - 'Times Visited' => 1, - 'City' => 'Denver', - 'State' => 'CO', - 'Restaurant' => 'Fake Restaurant 2' - 'Address' => '1555 Blake Street', - 'Phone Number' => '720.524.4345', - 'Cuisine' => 'Burgers', - 'Notes' => 'Order by phone. No delivery.', - 'Zip' => '80202' - ) + 'Times Visited' => 1, + 'City' => 'Denver', + 'State' => 'CO', + 'Restaurant' => 'Fake Restaurant 2', + 'Address' => '1555 Blake Street', + 'Phone Number' => '720.524.4345', + 'Cuisine' => 'Burgers', + 'Notes' => 'Order by phone. No delivery.', + 'Zip' => '80202' ); - $records[] = array( - 'table_id' => $tableId, - 'data' => array( - 'Times Visited' => 3, - 'City' => 'Denver', - 'State' => 'CO', - 'Restaurant' => 'Fake Restaurant 3' - 'Address' => '1555 Blake Street', - 'Phone Number' => '720.524.4345', - 'Cuisine' => 'Burgers', - 'Notes' => 'Order by phone. No delivery.', - 'Zip' => '80202' - ) + // 2nd record + $records[] = array( + 'Times Visited' => 3, + 'City' => 'Denver', + 'State' => 'CO', + 'Restaurant' => 'Fake Restaurant 3', + 'Address' => '1555 Blake Street', + 'Phone Number' => '720.524.4345', + 'Cuisine' => 'Burgers', + 'Notes' => 'Order by phone. No delivery.', + 'Zip' => '80202' ); - $tv->addRecords($records); + $tv->addRecords($tableId, $records); //************************ @@ -202,24 +198,25 @@ Any errors returned from the API server will be thrown as PHP exceptions. // Update multiple records // (batch updates) //************************ + $tableId = 62994; $records = array(); // 1st record $records[] = array( - 'id' => 105973376, // record id - 'data' => array( + 'id' => 105976918, // record id + 'fields' => array( 'Times Visited' => 12, 'Restaurant' => 'Fake Restaurant Updated2!!' ) ); // 2nd record $records[] = array( - 'id' => 105973377, // record id - 'data' => array( + 'id' => 105976919, // record id + 'fields' => array( 'Times Visited' => 13, 'Restaurant' => 'Fake Restaurant Updated3!!' ) ); - $tv->updateRecords($records); + $tv->updateRecords($tableId, $records); //************************ @@ -232,4 +229,5 @@ Any errors returned from the API server will be thrown as PHP exceptions. // Delete a multiple records // (batch delete) //************************ - $tv->deleteRecords(array(105973376, 105973377)); \ No newline at end of file + $tableId = 62994; + $tv->deleteRecords($tableId, array(105976918, 105976919)); \ No newline at end of file diff --git a/Trackvia/Api.php b/Trackvia/Api.php index 1bb86be..db4891d 100755 --- a/Trackvia/Api.php +++ b/Trackvia/Api.php @@ -16,6 +16,7 @@ class Api extends EventDispatcher const TABLES_URL = 'tables'; const VIEWS_URL = 'views'; const RECORDS_URL = 'records'; + const SEARCH_URL = 'search'; /** * Error message received back from api endpoint if access token is expired @@ -263,6 +264,12 @@ public function getTable($tableId) return $this->api($url, 'GET'); } + public function getTableForeignKeyValues($tableId, $fkId) + { + $url = self::BASE_URL . self::TABLES_URL .'/'. $tableId . '/foreign_keys/' . $fkId; + return $this->api($url, 'GET'); + } + /** * Get view data back for a view_id. * This will provide you with all the records under this view. @@ -296,30 +303,33 @@ public function getRecord($id) /** * Add a new record to a table * @param int $id - * @param array $data + * @param array $record * @return array */ - public function addRecord($id, $data) + public function addRecord($id, $record) { $url = self::BASE_URL . self::RECORDS_URL; - $data = array( - array( - 'table_id' => $id, - 'data' => $data - )); + 'table_id' => $id, + 'records' => array($record) + ); return $this->api($url, 'POST', json_encode($data), 'json'); } /** - * Add more than one record at once. Batch inserts. + * Add more than one record at once to a table. Batch inserts. * - * @param array $data + * @param int $tableId + * @param array $records * @return array */ - public function addRecords($data) + public function addRecords($tableId, $records) { $url = self::BASE_URL . self::RECORDS_URL; + $data = array( + 'table_id' => $tableId, + 'records' => $records + ); return $this->api($url, 'POST', json_encode($data), 'json'); } @@ -339,12 +349,17 @@ public function updateRecord($id, $data) /** * Update multiple records. * - * @param array $data + * @param int $tableId + * @param array $records * @return array */ - public function updateRecords($data) + public function updateRecords($tableId, $records) { $url = self::BASE_URL . self::RECORDS_URL; + $data = array( + 'table_id' => $tableId, + 'records' => $records + ); return $this->api($url, 'PUT', json_encode($data), 'json'); } @@ -362,11 +377,16 @@ public function deleteRecord($id) /** * Delete multiple records at once. Batch delete. * - * @param array $data + * @param int $tableId + * @param array $records */ - public function deleteRecords($data) + public function deleteRecords($tableId, $records) { $url = self::BASE_URL . self::RECORDS_URL; + $data = array( + 'table_id' => $tableId, + 'records' => $records + ); return $this->api($url, 'DELETE', json_encode($data), 'json'); } @@ -403,7 +423,7 @@ public function getForm($formId) */ public function search($tableId, $term) { - $url = self::BASE_URL . self::SEARCH_URL .'/'. $tableId .'/'. $term; + $url = self::BASE_URL . self::SEARCH_URL .'/'. $tableId .'/'. urlencode($term); return $this->api($url, 'GET'); } diff --git a/example.php b/example.php index 1a91dac..adf83ca 100755 --- a/example.php +++ b/example.php @@ -71,160 +71,4 @@ function load_saved_token_data() $authLog = new Log($tv->getAuthentication()); $log = new Log($tv); - -//********************* -// Get a list of dashboards -//********************* -$dashboards = $tv->getDashboards(); - -//********************* -// Get a single dashboard by dashboard_id -//********************* -$dashboards = $tv->getDashboard(2); - - -//********************* -// Get a list of all apps accessible by the user -//********************* -$apps = $tv->getApps(); - - -//********************* -// Get app data for a specific app_id -//********************* -$app = $tv->getApp(22087); - - -//********************* -// Get data for a table -//********************* -$table = $tv->getTable(62995); - - -//********************* -// Get a list of forms for a table -//********************* -$forms = $tv->getForms(62995); - -//********************* -// Get data for a specific form -//********************* -$form = $tv->getForm(3); - - -//********************* -// Get data for a view -//********************* -$view = $tv->getView(289592); - - -//************** -// Get a record -//************** -$record = $tv->getRecord(77113576); - - -//****************** -// Add a new record -//****************** -$tableId = 62994; -$record = array( - 'Times Visited' => 1, - 'City' => 'Denver', - 'State' => 'CO', - 'Menu Link' => 'http://www.google.com', - 'Restaurant' => 'Fake Restaurant 1', - 'Address' => '1555 Blake Street', - 'Hours' => '11am - 11pm', - 'Phone Number' => '720.524.4345', - 'Cuisine' => 'Burgers', - 'Notes' => 'Order by phone. No delivery.', - 'Zip' => '80202' -); -$tv->addRecord($tableId, $record); - - -//********************** -// Add multiple records -// (batch inserts) -//********************** -$tableId = 62994; -$records = array(); -$records[] = array( - 'table_id' => $tableId, - 'data' => array( - 'Times Visited' => 1, - 'City' => 'Denver', - 'State' => 'CO', - 'Restaurant' => 'Fake Restaurant 2' - 'Address' => '1555 Blake Street', - 'Phone Number' => '720.524.4345', - 'Cuisine' => 'Burgers', - 'Notes' => 'Order by phone. No delivery.', - 'Zip' => '80202' - ) -); -$records[] = array( - 'table_id' => $tableId, - 'data' => array( - 'Times Visited' => 3, - 'City' => 'Denver', - 'State' => 'CO', - 'Restaurant' => 'Fake Restaurant 3' - 'Address' => '1555 Blake Street', - 'Phone Number' => '720.524.4345', - 'Cuisine' => 'Burgers', - 'Notes' => 'Order by phone. No delivery.', - 'Zip' => '80202' - ) -); -$tv->addRecords($records); - - -//************************ -// Update a single record -//************************ -$recordId = 105973377; -$record = array( - 'Times Visited' => 5, - 'Restaurant' => 'Fake Restaurant Updated!!' -); -$tv->updateRecord($recordId, $record); - - -//************************ -// Update multiple records -// (batch updates) -//************************ -$records = array(); -// 1st record -$records[] = array( - 'id' => 105973376, // record id - 'data' => array( - 'Times Visited' => 12, - 'Restaurant' => 'Fake Restaurant Updated2!!' - ) -); -// 2nd record -$records[] = array( - 'id' => 105973377, // record id - 'data' => array( - 'Times Visited' => 13, - 'Restaurant' => 'Fake Restaurant Updated3!!' - ) -); -$tv->updateRecords($records); - - -//************************ -// Delete a single record -//************************ -$tv->deleteRecord(105973364); - - -//************************ -// Delete a multiple records -// (batch delete) -//************************ -$tv->deleteRecords(array(105973376, 105973377)); */ \ No newline at end of file From 4482844c5f61a3336f2d6f92e5441716b00f0650 Mon Sep 17 00:00:00 2001 From: Chris Oake Date: Tue, 5 Feb 2013 09:35:02 -0700 Subject: [PATCH 11/14] few more tweaks --- Trackvia/Api.php | 10 +++++----- Trackvia/Authentication.php | 4 ++++ Trackvia/Log.php | 24 +++++++++++++++++++++++- Trackvia/Request.php | 1 - 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/Trackvia/Api.php b/Trackvia/Api.php index db4891d..1e45cf4 100755 --- a/Trackvia/Api.php +++ b/Trackvia/Api.php @@ -16,7 +16,7 @@ class Api extends EventDispatcher const TABLES_URL = 'tables'; const VIEWS_URL = 'views'; const RECORDS_URL = 'records'; - const SEARCH_URL = 'search'; + const SEARCH_URL = 'search'; /** * Error message received back from api endpoint if access token is expired @@ -68,7 +68,7 @@ public function authenticate() } /** - * Method the handle the new_token even trigger by the authentication class + * Method to handle the new_token even trigger by the authentication class * Bubble up the event with token data for the client. */ public function onNewAccessToken($data) @@ -132,11 +132,11 @@ private function checkResponse() * * @param string $url * @param string $httpMethod The http method to use with this request - * @param array $data Optional array of data to send with request + * @param string $data Optional data to send with request * @param string $contentType * @return array The json parsed response from the server */ - private function api($url, $httpMethod = 'GET', $data = array(), $contentType = null) + private function api($url, $httpMethod = 'GET', $data = null, $contentType = null) { // trigger an event $this->trigger('api_request_init', array('url' => $url)); @@ -160,7 +160,7 @@ private function api($url, $httpMethod = 'GET', $data = array(), $contentType = // add the access token onto the url $url = $url . '?access_token='.$accessToken; - $this->trigger('api_request_send', array('url' => $url, 'http_method' => $httpMethod)); + $this->trigger('api_request_send', array('url' => $url, 'http_method' => $httpMethod, 'data' => $data)); $this->request ->setMethod($httpMethod) diff --git a/Trackvia/Authentication.php b/Trackvia/Authentication.php index 7f7b7e7..e5a822b 100755 --- a/Trackvia/Authentication.php +++ b/Trackvia/Authentication.php @@ -340,13 +340,17 @@ public function authenticate() } else { if (!$this->hasRefreshToken()) { // no tokens available, so we need to request new ones + $this->trigger('no_authentication_tokens'); // check for user credentials flow first if ($this->hasUserCreds()) { + $this->trigger('authenticate_with_user_creds'); $response = $this->requestTokenWithUserCreds( $this->userCreds['username'], $this->userCreds['password'] ); + } else { + $this->trigger('no_authentication'); } //TODO add support for redirecting user to auth trackvia endpoint diff --git a/Trackvia/Log.php b/Trackvia/Log.php index 3552645..6c03d2d 100755 --- a/Trackvia/Log.php +++ b/Trackvia/Log.php @@ -24,7 +24,10 @@ public function __construct($subject) 'has_refresh_token', 'request_token_with_user_creds', 'request_token_with_refresh_token', - 'refresh_token_expired' + 'refresh_token_expired', + 'no_authentication_tokens', + 'authenticate_with_user_creds', + 'no_authentication' )); } @@ -68,6 +71,10 @@ public function on_api_request_send($data) $url = $data['url']; $method = $data['http_method']; $this->logInfo("Sending $method request to url \"$url\""); + + if (isset($data['data'])) { + $this->logInfo("Sending request body - " . $data['data']); + } } public function on_api_request_complete($data) @@ -107,4 +114,19 @@ public function on_refresh_token_expired() { $this->logInfo("Refresh token is expired"); } + + public function on_no_authentication_tokens() + { + $this->logInfo("No authentication tokens available"); + } + + public function on_authenticate_with_user_creds() + { + $this->logInfo("Authenticating with user credentials"); + } + + public function on_no_authentication() + { + $this->logInfo("No way to authenticate"); + } } \ No newline at end of file diff --git a/Trackvia/Request.php b/Trackvia/Request.php index a3fbc07..d1f9a38 100755 --- a/Trackvia/Request.php +++ b/Trackvia/Request.php @@ -87,7 +87,6 @@ public function send($url) foreach ($this->headers as $name => $value) { $headers[] = "$name: $value"; } - print_r($headers); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); } From 2ab5706f577474ab124b0ba9a233d533f4819df0 Mon Sep 17 00:00:00 2001 From: Scott Fennell Date: Fri, 8 Feb 2013 10:57:40 -0700 Subject: [PATCH 12/14] Set the BASE_URL to api.trackvia.com --- Trackvia/Api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Trackvia/Api.php b/Trackvia/Api.php index 1e45cf4..2e84edf 100755 --- a/Trackvia/Api.php +++ b/Trackvia/Api.php @@ -7,7 +7,7 @@ class Api extends EventDispatcher { - const BASE_URL = 'https://api.trackviadev.com/'; + const BASE_URL = 'https://api.trackvia.com/'; // URLs for API endpoints const DASHBOARDS_URL = 'dashboards'; From a83769c5687b191850abeae22061d4e4165da7b9 Mon Sep 17 00:00:00 2001 From: Scott Fennell Date: Fri, 8 Feb 2013 11:33:52 -0700 Subject: [PATCH 13/14] Update the example code to display a list of a users TrackVia apps. Added an advanced example to show other authentication methods. --- advanced_example.php | 90 ++++++++++++++++++++++++++++++ example.php | 129 +++++++++++++++++++++++-------------------- 2 files changed, 158 insertions(+), 61 deletions(-) create mode 100644 advanced_example.php diff --git a/advanced_example.php b/advanced_example.php new file mode 100644 index 0000000..b9a9d75 --- /dev/null +++ b/advanced_example.php @@ -0,0 +1,90 @@ + CLIENT_ID, + 'client_secret' => CLIENT_SECRET, +)); + +// load the saved token for this user +function load_saved_token_data() +{ + //=========== + // code to load the token from your database + //=========== + + // return array( + // 'access_token' => 'user_access_token', + // 'refresh_token' => 'user_refresh_token', + // 'expires_at' => 'expires_timestamp' + // ); +} +$savedToken = load_saved_token_data(); + +// If there is saved token data to use, set it now +if (!empty($savedToken) && isset($savedToken['access_token'])) { + + $tv->setTokenData(array( + 'access_token' => $savedToken['access_token'], + 'refresh_token' => $savedToken['refresh_token'], + 'expires_at' => $savedToken['expires_at'] + )); + +} else { + // No token data. + // So you need to get the user credentials for authentication. + $tv->setUserCredentials(USERNAME, PASSWORD); +} + +// extra parameters to pass in for the "new_token" event callback +$extraParams = array('extra_params' => 'this can be whatever you want'); + +// attach a listener function for when a new token is generated so you can save it to a database +$tv->on('new_token', function ($tokenData, $extraParams) { + $accessToken = $tokenData['access_token']; + $refreshToken = $tokenData['refresh_token']; + + // timestamp when the access token expires + $expiresAt = $tokenData['expires_at']; + + //============ + // code to save token data to your database + //============ + +}, $extraParams); + +/* + +//********************* +// Setup the logger for debugging (optional) +//********************* +$authLog = new Log($tv->getAuthentication()); +$log = new Log($tv); + +*/ + +/** + * Get a list of your apps + */ +$apps = $tv->getApps(); +var_dump($apps); \ No newline at end of file diff --git a/example.php b/example.php index adf83ca..81f6e81 100755 --- a/example.php +++ b/example.php @@ -5,70 +5,77 @@ use Trackvia\Api; use Trackvia\Log; +define('CLIENT_ID', ''); +define('CLIENT_SECRET', ''); +define('USERNAME', ''); +define('PASSWORD', ''); + // Create a TrackviaApi object with your clientId and secret. // The client_id and secret are only used when you need to request a new access token. $tv = new Api(array( - 'client_id' => 'your_client_id', - 'client_secret' => 'your_client_secret' - - // You can optionally pass in the user credentials here too - // 'username' => 'sample_username', - // 'password' => 'sample_password' + 'client_id' => CLIENT_ID, + 'client_secret' => CLIENT_SECRET, + 'username' => USERNAME, + 'password' => PASSWORD )); -// load the saved token for this user -function load_saved_token_data() -{ - //=========== - // code to load the token from your database - //=========== - - // return array( - // 'access_token' => 'user_access_token', - // 'refresh_token' => 'user_refresh_token', - // 'expires_at' => 'expires_timestamp' - // ); -} -$savedToken = load_saved_token_data(); - -// If there is saved token data to use, set it now -if (!empty($savedToken) && isset($savedToken['access_token'])) { - - $tv->setTokenData(array( - 'access_token' => $savedToken['access_token'], - 'refresh_token' => $savedToken['refresh_token'], - 'expires_at' => $savedToken['expires_at'] - )); - -} else { - // No token data. - // So you need to get the user credentials for authentication. - $tv->setUserCreds('sample_username', 'sample_password'); -} - -// extra parameters to pass in for the "new_token" event callback -$extraParams = array('extra_params' => 'this can be whatever you want'); - -// attach a listener function for when a new token is generated so you can save it to a database -$tv->on('new_token', function ($tokenData, $extraParams) { - $accessToken = $tokenData['access_token']; - $refreshToken = $tokenData['refresh_token']; - - // timestamp when the access token expires - $expiresAt = $tokenData['expires_at']; - - //============ - // code to save token data to your database - //============ - -}, $extraParams); - -/* - -//********************* -// Setup the logger for debugging (optional) -//********************* -$authLog = new Log($tv->getAuthentication()); -$log = new Log($tv); +?> + + + + + Trackvia API v2 Example + + + +

Trackvia API v2 Example

+ +

Quick Start

+

+ Your example code requires your client_id, client_secret, username and password. You can get these + at the Trackvia account settings page. + Your username and password are the username and password you use to login to TrackVia. +

+

+ Once you have your credentials, update them in the top of the example.php file. +

+ +

+ Below is a simple example to show you how to get a list of all your TrackVia apps. +

+ getApps() will + * get a list of all the apps that you have in your account along with the app id. + */ + $apps = $tv->getApps(); + ?> +

Your Trackvia Apps

+
    + 432 + * name => "Some App" + * ), + * array( + * id => 3543 + * name => "Some other app" + * ) + * ) + */ + foreach ($apps as $app) { + echo "
  • ".$app['name']."
  • "; + } + ?> +
+ + + -*/ \ No newline at end of file From 5d595d0258d24cda7ab5de95198933170bae57c1 Mon Sep 17 00:00:00 2001 From: Scott Fennell Date: Thu, 28 Jan 2016 23:10:51 -0700 Subject: [PATCH 14/14] Updating search to include the ability to POST search options and use a view --- Trackvia/Api.php | 73 ++++++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/Trackvia/Api.php b/Trackvia/Api.php index 2e84edf..2cb9886 100755 --- a/Trackvia/Api.php +++ b/Trackvia/Api.php @@ -42,9 +42,9 @@ class Api extends EventDispatcher /** * Whether or not the current access token is expired. - * This gets flagged true when an api request is made and + * This gets flagged true when an api request is made and * the response comes back as an error indicating expired token. - * + * * @var boolean */ private $isTokenExpired = false; @@ -102,10 +102,10 @@ public function getRequest() /** * Check if the response failed and if the token is expired. - * + * * Any errors returned from the API server will be thrown as an Exception. - * - * @param array $response + * + * @param array $response * @return boolean */ private function checkResponse() @@ -129,7 +129,7 @@ private function checkResponse() /** * Make an api request. - * + * * @param string $url * @param string $httpMethod The http method to use with this request * @param string $data Optional data to send with request @@ -161,7 +161,7 @@ private function api($url, $httpMethod = 'GET', $data = null, $contentType = nul $url = $url . '?access_token='.$accessToken; $this->trigger('api_request_send', array('url' => $url, 'http_method' => $httpMethod, 'data' => $data)); - + $this->request ->setMethod($httpMethod) ->setData($data); @@ -194,10 +194,10 @@ private function api($url, $httpMethod = 'GET', $data = null, $contentType = nul /** * Get a list of all dashboards, or a single dashboard. - * + * * Optional id parameter lets you specify one dashboard if you want. * Leave is empty to get all dashboards back. - * + * * @param int $id * @return array Array of dashboard data returned from the api */ @@ -205,13 +205,13 @@ public function getDashboards($id = null) { // build the url $url = self::BASE_URL . self::DASHBOARDS_URL . ($id ? '/'.$id : ''); - + return $this->api($url, 'GET'); } /** * Get data for a single dashboard. - * + * * @param int $id * @return array */ @@ -222,10 +222,10 @@ public function getDashboard($id) /** * Get a list of all apps, or a single app. - * + * * Optional app_id parameter lets you specify one app if you want. * Leave is empty to get all apps back. - * + * * @param int $appId * @return array Array of app data returned from the api */ @@ -233,14 +233,14 @@ public function getApps($appId = null) { // build the url $url = self::BASE_URL . self::APPS_URL . ($appId ? '/'.$appId : ''); - + return $this->api($url, 'GET'); } /** * Get data for a single app by app_id. * This will provide you with all the tables available for this app. - * + * * @param int $appId * @return array Array of app data returned fromt the api */ @@ -252,7 +252,7 @@ public function getApp($appId) /** * Get table data back for a table_id. * This will provide you all the views available for this table. - * + * * @param int $tableId * @return array Array of table data returned from the api */ @@ -260,7 +260,7 @@ public function getTable($tableId) { // build the url $url = self::BASE_URL . self::TABLES_URL .'/'. $tableId; - + return $this->api($url, 'GET'); } @@ -273,7 +273,7 @@ public function getTableForeignKeyValues($tableId, $fkId) /** * Get view data back for a view_id. * This will provide you with all the records under this view. - * + * * @param int $viewId * @return array Array of view data returned from the api */ @@ -281,14 +281,14 @@ public function getView($viewId) { // build the url $url = self::BASE_URL . self::VIEWS_URL .'/'. $viewId; - + return $this->api($url, 'GET'); } /** * Get Record data back for a record_id. * This will provide you with all the column data for a record. - * + * * @param int $id * @return array Array of Record data returned from the api */ @@ -296,7 +296,7 @@ public function getRecord($id) { // build the url $url = self::BASE_URL . self::RECORDS_URL .'/'. $id; - + return $this->api($url, 'GET'); } @@ -318,7 +318,7 @@ public function addRecord($id, $record) /** * Add more than one record at once to a table. Batch inserts. - * + * * @param int $tableId * @param array $records * @return array @@ -335,7 +335,7 @@ public function addRecords($tableId, $records) /** * Update a single record. - * + * * @param int $id * @param array $data * @return array @@ -348,7 +348,7 @@ public function updateRecord($id, $data) /** * Update multiple records. - * + * * @param int $tableId * @param array $records * @return array @@ -365,7 +365,7 @@ public function updateRecords($tableId, $records) /** * Delete a record by id. - * + * * @param int $id */ public function deleteRecord($id) @@ -376,7 +376,7 @@ public function deleteRecord($id) /** * Delete multiple records at once. Batch delete. - * + * * @param int $tableId * @param array $records */ @@ -398,7 +398,7 @@ public function deleteRecords($tableId, $records) public function getForms($tableId) { $url = self::BASE_URL . self::TABLES_URL .'/'. $tableId .'/'. self::FORMS_URL; - + return $this->api($url, 'GET'); } @@ -410,21 +410,28 @@ public function getForms($tableId) public function getForm($formId) { $url = self::BASE_URL . self::FORMS_URL .'/'. $formId; - + return $this->api($url, 'GET'); } /** * Search for records on a table. - * + * * @param int $tableId * @param string $term + * @param int $viewId [optional] + * @param string $method (POST|GET) * @return array */ - public function search($tableId, $term) + public function search($tableId, $term, $viewId = null, $method = 'GET') { - $url = self::BASE_URL . self::SEARCH_URL .'/'. $tableId .'/'. urlencode($term); - return $this->api($url, 'GET'); + if (is_null($viewId)) { + $url = self::BASE_URL . self::SEARCH_URL .'/'. $tableId .'/'. urlencode($term); + } else { + $url = self::BASE_URL . self::SEARCH_URL .'/'. $tableId .'/'.$viewId.'/'. urlencode($term); + } + return $this->api($url, $method); } + -} \ No newline at end of file +}