Merge pull request #42 from cytopia/DVL-007

DVL-007 Fix gitignore and re-add ignored files
This commit is contained in:
cytopia 2017-04-22 13:26:49 +02:00 committed by GitHub
commit e344ff2243
37 changed files with 2750 additions and 15 deletions

View File

@ -0,0 +1,16 @@
<?php
namespace Guzzle\Log;
/**
* Adapter class that allows Guzzle to log data using various logging implementations
*/
abstract class AbstractLogAdapter implements LogAdapterInterface
{
protected $log;
public function getLogObject()
{
return $this->log;
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace Guzzle\Log;
/**
* Stores all log messages in an array
*/
class ArrayLogAdapter implements LogAdapterInterface
{
protected $logs = array();
public function log($message, $priority = LOG_INFO, $extras = array())
{
$this->logs[] = array('message' => $message, 'priority' => $priority, 'extras' => $extras);
}
/**
* Get logged entries
*
* @return array
*/
public function getLogs()
{
return $this->logs;
}
/**
* Clears logged entries
*/
public function clearLogs()
{
$this->logs = array();
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace Guzzle\Log;
/**
* Logs messages using Closures. Closures combined with filtering can trigger application events based on log messages.
*/
class ClosureLogAdapter extends AbstractLogAdapter
{
public function __construct($logObject)
{
if (!is_callable($logObject)) {
throw new \InvalidArgumentException('Object must be callable');
}
$this->log = $logObject;
}
public function log($message, $priority = LOG_INFO, $extras = array())
{
call_user_func($this->log, $message, $priority, $extras);
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace Guzzle\Log;
/**
* Adapter class that allows Guzzle to log data to various logging implementations.
*/
interface LogAdapterInterface
{
/**
* Log a message at a priority
*
* @param string $message Message to log
* @param integer $priority Priority of message (use the \LOG_* constants of 0 - 7)
* @param array $extras Extra information to log in event
*/
public function log($message, $priority = LOG_INFO, $extras = array());
}

View File

@ -0,0 +1,179 @@
<?php
namespace Guzzle\Log;
use Guzzle\Http\Curl\CurlHandle;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Message\Response;
/**
* Message formatter used in various places in the framework
*
* Format messages using a template that can contain the the following variables:
*
* - {request}: Full HTTP request message
* - {response}: Full HTTP response message
* - {ts}: Timestamp
* - {host}: Host of the request
* - {method}: Method of the request
* - {url}: URL of the request
* - {host}: Host of the request
* - {protocol}: Request protocol
* - {version}: Protocol version
* - {resource}: Resource of the request (path + query + fragment)
* - {port}: Port of the request
* - {hostname}: Hostname of the machine that sent the request
* - {code}: Status code of the response (if available)
* - {phrase}: Reason phrase of the response (if available)
* - {curl_error}: Curl error message (if available)
* - {curl_code}: Curl error code (if available)
* - {curl_stderr}: Curl standard error (if available)
* - {connect_time}: Time in seconds it took to establish the connection (if available)
* - {total_time}: Total transaction time in seconds for last transfer (if available)
* - {req_header_*}: Replace `*` with the lowercased name of a request header to add to the message
* - {res_header_*}: Replace `*` with the lowercased name of a response header to add to the message
* - {req_body}: Request body
* - {res_body}: Response body
*/
class MessageFormatter
{
const DEFAULT_FORMAT = "{hostname} {req_header_User-Agent} - [{ts}] \"{method} {resource} {protocol}/{version}\" {code} {res_header_Content-Length}";
const DEBUG_FORMAT = ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{curl_stderr}";
const SHORT_FORMAT = '[{ts}] "{method} {resource} {protocol}/{version}" {code}';
/**
* @var string Template used to format log messages
*/
protected $template;
/**
* @param string $template Log message template
*/
public function __construct($template = self::DEFAULT_FORMAT)
{
$this->template = $template ?: self::DEFAULT_FORMAT;
}
/**
* Set the template to use for logging
*
* @param string $template Log message template
*
* @return self
*/
public function setTemplate($template)
{
$this->template = $template;
return $this;
}
/**
* Returns a formatted message
*
* @param RequestInterface $request Request that was sent
* @param Response $response Response that was received
* @param CurlHandle $handle Curl handle associated with the message
* @param array $customData Associative array of custom template data
*
* @return string
*/
public function format(
RequestInterface $request,
Response $response = null,
CurlHandle $handle = null,
array $customData = array()
) {
$cache = $customData;
return preg_replace_callback(
'/{\s*([A-Za-z_\-\.0-9]+)\s*}/',
function (array $matches) use ($request, $response, $handle, &$cache) {
if (array_key_exists($matches[1], $cache)) {
return $cache[$matches[1]];
}
$result = '';
switch ($matches[1]) {
case 'request':
$result = (string) $request;
break;
case 'response':
$result = (string) $response;
break;
case 'req_body':
$result = $request instanceof EntityEnclosingRequestInterface
? (string) $request->getBody() : '';
break;
case 'res_body':
$result = $response ? $response->getBody(true) : '';
break;
case 'ts':
$result = gmdate('c');
break;
case 'method':
$result = $request->getMethod();
break;
case 'url':
$result = (string) $request->getUrl();
break;
case 'resource':
$result = $request->getResource();
break;
case 'protocol':
$result = 'HTTP';
break;
case 'version':
$result = $request->getProtocolVersion();
break;
case 'host':
$result = $request->getHost();
break;
case 'hostname':
$result = gethostname();
break;
case 'port':
$result = $request->getPort();
break;
case 'code':
$result = $response ? $response->getStatusCode() : '';
break;
case 'phrase':
$result = $response ? $response->getReasonPhrase() : '';
break;
case 'connect_time':
$result = $handle && $handle->getInfo(CURLINFO_CONNECT_TIME)
? $handle->getInfo(CURLINFO_CONNECT_TIME)
: ($response ? $response->getInfo('connect_time') : '');
break;
case 'total_time':
$result = $handle && $handle->getInfo(CURLINFO_TOTAL_TIME)
? $handle->getInfo(CURLINFO_TOTAL_TIME)
: ($response ? $response->getInfo('total_time') : '');
break;
case 'curl_error':
$result = $handle ? $handle->getError() : '';
break;
case 'curl_code':
$result = $handle ? $handle->getErrorNo() : '';
break;
case 'curl_stderr':
$result = $handle ? $handle->getStderr() : '';
break;
default:
if (strpos($matches[1], 'req_header_') === 0) {
$result = $request->getHeader(substr($matches[1], 11));
} elseif ($response && strpos($matches[1], 'res_header_') === 0) {
$result = $response->getHeader(substr($matches[1], 11));
}
}
$cache[$matches[1]] = $result;
return $result;
},
$this->template
);
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace Guzzle\Log;
use Monolog\Logger;
/**
* @deprecated
* @codeCoverageIgnore
*/
class MonologLogAdapter extends AbstractLogAdapter
{
/**
* syslog to Monolog mappings
*/
private static $mapping = array(
LOG_DEBUG => Logger::DEBUG,
LOG_INFO => Logger::INFO,
LOG_WARNING => Logger::WARNING,
LOG_ERR => Logger::ERROR,
LOG_CRIT => Logger::CRITICAL,
LOG_ALERT => Logger::ALERT
);
public function __construct(Logger $logObject)
{
$this->log = $logObject;
}
public function log($message, $priority = LOG_INFO, $extras = array())
{
$this->log->addRecord(self::$mapping[$priority], $message, $extras);
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace Guzzle\Log;
use Psr\Log\LogLevel;
use Psr\Log\LoggerInterface;
/**
* PSR-3 log adapter
*
* @link https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
*/
class PsrLogAdapter extends AbstractLogAdapter
{
/**
* syslog to PSR-3 mappings
*/
private static $mapping = array(
LOG_DEBUG => LogLevel::DEBUG,
LOG_INFO => LogLevel::INFO,
LOG_WARNING => LogLevel::WARNING,
LOG_ERR => LogLevel::ERROR,
LOG_CRIT => LogLevel::CRITICAL,
LOG_ALERT => LogLevel::ALERT
);
public function __construct(LoggerInterface $logObject)
{
$this->log = $logObject;
}
public function log($message, $priority = LOG_INFO, $extras = array())
{
$this->log->log(self::$mapping[$priority], $message, $extras);
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace Guzzle\Log;
use Guzzle\Common\Version;
/**
* Adapts a Zend Framework 1 logger object
* @deprecated
* @codeCoverageIgnore
*/
class Zf1LogAdapter extends AbstractLogAdapter
{
public function __construct(\Zend_Log $logObject)
{
$this->log = $logObject;
Version::warn(__CLASS__ . ' is deprecated');
}
public function log($message, $priority = LOG_INFO, $extras = array())
{
$this->log->log($message, $priority, $extras);
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace Guzzle\Log;
use Zend\Log\Logger;
/**
* Adapts a Zend Framework 2 logger object
*/
class Zf2LogAdapter extends AbstractLogAdapter
{
public function __construct(Logger $logObject)
{
$this->log = $logObject;
}
public function log($message, $priority = LOG_INFO, $extras = array())
{
$this->log->log($priority, $message, $extras);
}
}

View File

@ -0,0 +1,29 @@
{
"name": "guzzle/log",
"description": "Guzzle log adapter component",
"homepage": "http://guzzlephp.org/",
"keywords": ["log", "adapter", "guzzle"],
"license": "MIT",
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"require": {
"php": ">=5.3.2"
},
"autoload": {
"psr-0": { "Guzzle\\Log": "" }
},
"suggest": {
"guzzle/http": "self.version"
},
"target-dir": "Guzzle/Log",
"extra": {
"branch-alias": {
"dev-master": "3.7-dev"
}
}
}

View File

@ -0,0 +1,161 @@
<?php
namespace Guzzle\Plugin\Log;
use Guzzle\Common\Event;
use Guzzle\Log\LogAdapterInterface;
use Guzzle\Log\MessageFormatter;
use Guzzle\Log\ClosureLogAdapter;
use Guzzle\Http\EntityBody;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Plugin class that will add request and response logging to an HTTP request.
*
* The log plugin uses a message formatter that allows custom messages via template variable substitution.
*
* @see MessageLogger for a list of available log template variable substitutions
*/
class LogPlugin implements EventSubscriberInterface
{
/** @var LogAdapterInterface Adapter responsible for writing log data */
protected $logAdapter;
/** @var MessageFormatter Formatter used to format messages before logging */
protected $formatter;
/** @var bool Whether or not to wire request and response bodies */
protected $wireBodies;
/**
* @param LogAdapterInterface $logAdapter Adapter object used to log message
* @param string|MessageFormatter $formatter Formatter used to format log messages or the formatter template
* @param bool $wireBodies Set to true to track request and response bodies using a temporary
* buffer if the bodies are not repeatable.
*/
public function __construct(
LogAdapterInterface $logAdapter,
$formatter = null,
$wireBodies = false
) {
$this->logAdapter = $logAdapter;
$this->formatter = $formatter instanceof MessageFormatter ? $formatter : new MessageFormatter($formatter);
$this->wireBodies = $wireBodies;
}
/**
* Get a log plugin that outputs full request, response, and curl error information to stderr
*
* @param bool $wireBodies Set to false to disable request/response body output when they use are not repeatable
* @param resource $stream Stream to write to when logging. Defaults to STDERR when it is available
*
* @return self
*/
public static function getDebugPlugin($wireBodies = true, $stream = null)
{
if ($stream === null) {
if (defined('STDERR')) {
$stream = STDERR;
} else {
$stream = fopen('php://output', 'w');
}
}
return new self(new ClosureLogAdapter(function ($m) use ($stream) {
fwrite($stream, $m . PHP_EOL);
}), "# Request:\n{request}\n\n# Response:\n{response}\n\n# Errors: {curl_code} {curl_error}", $wireBodies);
}
public static function getSubscribedEvents()
{
return array(
'curl.callback.write' => array('onCurlWrite', 255),
'curl.callback.read' => array('onCurlRead', 255),
'request.before_send' => array('onRequestBeforeSend', 255),
'request.sent' => array('onRequestSent', 255)
);
}
/**
* Event triggered when curl data is read from a request
*
* @param Event $event
*/
public function onCurlRead(Event $event)
{
// Stream the request body to the log if the body is not repeatable
if ($wire = $event['request']->getParams()->get('request_wire')) {
$wire->write($event['read']);
}
}
/**
* Event triggered when curl data is written to a response
*
* @param Event $event
*/
public function onCurlWrite(Event $event)
{
// Stream the response body to the log if the body is not repeatable
if ($wire = $event['request']->getParams()->get('response_wire')) {
$wire->write($event['write']);
}
}
/**
* Called before a request is sent
*
* @param Event $event
*/
public function onRequestBeforeSend(Event $event)
{
if ($this->wireBodies) {
$request = $event['request'];
// Ensure that curl IO events are emitted
$request->getCurlOptions()->set('emit_io', true);
// We need to make special handling for content wiring and non-repeatable streams.
if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()
&& (!$request->getBody()->isSeekable() || !$request->getBody()->isReadable())
) {
// The body of the request cannot be recalled so logging the body will require us to buffer it
$request->getParams()->set('request_wire', EntityBody::factory());
}
if (!$request->getResponseBody()->isRepeatable()) {
// The body of the response cannot be recalled so logging the body will require us to buffer it
$request->getParams()->set('response_wire', EntityBody::factory());
}
}
}
/**
* Triggers the actual log write when a request completes
*
* @param Event $event
*/
public function onRequestSent(Event $event)
{
$request = $event['request'];
$response = $event['response'];
$handle = $event['handle'];
if ($wire = $request->getParams()->get('request_wire')) {
$request = clone $request;
$request->setBody($wire);
}
if ($wire = $request->getParams()->get('response_wire')) {
$response = clone $response;
$response->setBody($wire);
}
// Send the log message to the adapter, adding a category and host
$priority = $response && $response->isError() ? LOG_ERR : LOG_DEBUG;
$message = $this->formatter->format($request, $response, $handle);
$this->logAdapter->log($message, $priority, array(
'request' => $request,
'response' => $response,
'handle' => $handle
));
}
}

View File

@ -0,0 +1,28 @@
{
"name": "guzzle/plugin-log",
"description": "Guzzle log plugin for over the wire logging",
"homepage": "http://guzzlephp.org/",
"keywords": ["plugin", "log", "guzzle"],
"license": "MIT",
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"require": {
"php": ">=5.3.2",
"guzzle/http": "self.version",
"guzzle/log": "self.version"
},
"autoload": {
"psr-0": { "Guzzle\\Plugin\\Log": "" }
},
"target-dir": "Guzzle/Plugin/Log",
"extra": {
"branch-alias": {
"dev-master": "3.7-dev"
}
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace Guzzle\Tests\Log;
use Guzzle\Log\ArrayLogAdapter;
class ArrayLogAdapterTest extends \Guzzle\Tests\GuzzleTestCase
{
public function testLog()
{
$adapter = new ArrayLogAdapter();
$adapter->log('test', \LOG_NOTICE, '127.0.0.1');
$this->assertEquals(array(array('message' => 'test', 'priority' => \LOG_NOTICE, 'extras' => '127.0.0.1')), $adapter->getLogs());
}
public function testClearLog()
{
$adapter = new ArrayLogAdapter();
$adapter->log('test', \LOG_NOTICE, '127.0.0.1');
$adapter->clearLogs();
$this->assertEquals(array(), $adapter->getLogs());
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace Guzzle\Tests\Log;
use Guzzle\Log\ClosureLogAdapter;
/**
* @covers Guzzle\Log\ClosureLogAdapter
*/
class ClosureLogAdapterTest extends \Guzzle\Tests\GuzzleTestCase
{
public function testClosure()
{
$that = $this;
$modified = null;
$this->adapter = new ClosureLogAdapter(function($message, $priority, $extras = null) use ($that, &$modified) {
$modified = array($message, $priority, $extras);
});
$this->adapter->log('test', LOG_NOTICE, '127.0.0.1');
$this->assertEquals(array('test', LOG_NOTICE, '127.0.0.1'), $modified);
}
/**
* @expectedException InvalidArgumentException
*/
public function testThrowsExceptionWhenNotCallable()
{
$this->adapter = new ClosureLogAdapter(123);
}
}

View File

@ -0,0 +1,143 @@
<?php
namespace Guzzle\Tests\Log;
use Guzzle\Http\Client;
use Guzzle\Http\Curl\CurlHandle;
use Guzzle\Http\Message\EntityEnclosingRequest;
use Guzzle\Http\EntityBody;
use Guzzle\Http\Message\Response;
use Guzzle\Log\MessageFormatter;
use Guzzle\Plugin\Log\LogPlugin;
use Guzzle\Log\ClosureLogAdapter;
/**
* @covers Guzzle\Log\MessageFormatter
*/
class MessageFormatterTest extends \Guzzle\Tests\GuzzleTestCase
{
protected $request;
protected $response;
protected $handle;
public function setUp()
{
$this->request = new EntityEnclosingRequest('POST', 'http://foo.com?q=test', array(
'X-Foo' => 'bar',
'Authorization' => 'Baz'
));
$this->request->setBody(EntityBody::factory('Hello'));
$this->response = new Response(200, array(
'X-Test' => 'Abc'
), 'Foo');
$this->handle = $this->getMockBuilder('Guzzle\Http\Curl\CurlHandle')
->disableOriginalConstructor()
->setMethods(array('getError', 'getErrorNo', 'getStderr', 'getInfo'))
->getMock();
$this->handle->expects($this->any())
->method('getError')
->will($this->returnValue('e'));
$this->handle->expects($this->any())
->method('getErrorNo')
->will($this->returnValue('123'));
$this->handle->expects($this->any())
->method('getStderr')
->will($this->returnValue('testing'));
$this->handle->expects($this->any())
->method('getInfo')
->will($this->returnValueMap(array(
array(CURLINFO_CONNECT_TIME, '123'),
array(CURLINFO_TOTAL_TIME, '456')
)));
}
public function logProvider()
{
return array(
// Uses the cache for the second time
array('{method} - {method}', 'POST - POST'),
array('{url}', 'http://foo.com?q=test'),
array('{port}', '80'),
array('{resource}', '/?q=test'),
array('{host}', 'foo.com'),
array('{hostname}', gethostname()),
array('{protocol}/{version}', 'HTTP/1.1'),
array('{code} {phrase}', '200 OK'),
array('{req_header_Foo}', ''),
array('{req_header_X-Foo}', 'bar'),
array('{req_header_Authorization}', 'Baz'),
array('{res_header_foo}', ''),
array('{res_header_X-Test}', 'Abc'),
array('{req_body}', 'Hello'),
array('{res_body}', 'Foo'),
array('{curl_stderr}', 'testing'),
array('{curl_error}', 'e'),
array('{curl_code}', '123'),
array('{connect_time}', '123'),
array('{total_time}', '456')
);
}
/**
* @dataProvider logProvider
*/
public function testFormatsMessages($template, $output)
{
$formatter = new MessageFormatter($template);
$this->assertEquals($output, $formatter->format($this->request, $this->response, $this->handle));
}
public function testFormatsRequestsAndResponses()
{
$formatter = new MessageFormatter();
$formatter->setTemplate('{request}{response}');
$this->assertEquals($this->request . $this->response, $formatter->format($this->request, $this->response));
}
public function testAddsTimestamp()
{
$formatter = new MessageFormatter('{ts}');
$this->assertNotEmpty($formatter->format($this->request, $this->response));
}
public function testUsesResponseWhenNoHandleAndGettingCurlInformation()
{
$formatter = new MessageFormatter('{connect_time}/{total_time}');
$response = $this->getMockBuilder('Guzzle\Http\Message\Response')
->setConstructorArgs(array(200))
->setMethods(array('getInfo'))
->getMock();
$response->expects($this->exactly(2))
->method('getInfo')
->will($this->returnValueMap(array(
array('connect_time', '1'),
array('total_time', '2'),
)));
$this->assertEquals('1/2', $formatter->format($this->request, $response));
}
public function testUsesEmptyStringWhenNoHandleAndNoResponse()
{
$formatter = new MessageFormatter('{connect_time}/{total_time}');
$this->assertEquals('/', $formatter->format($this->request));
}
public function testInjectsTotalTime()
{
$out = '';
$formatter = new MessageFormatter('{connect_time}/{total_time}');
$adapter = new ClosureLogAdapter(function ($m) use (&$out) { $out .= $m; });
$log = new LogPlugin($adapter, $formatter);
$this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nHI");
$client = new Client($this->getServer()->getUrl());
$client->addSubscriber($log);
$client->get('/')->send();
$this->assertNotEquals('/', $out);
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace Guzzle\Tests\Log;
use Guzzle\Log\PsrLogAdapter;
use Monolog\Logger;
use Monolog\Handler\TestHandler;
/**
* @covers Guzzle\Log\PsrLogAdapter
* @covers Guzzle\Log\AbstractLogAdapter
*/
class PsrLogAdapterTest extends \Guzzle\Tests\GuzzleTestCase
{
public function testLogsMessagesToAdaptedObject()
{
$log = new Logger('test');
$handler = new TestHandler();
$log->pushHandler($handler);
$adapter = new PsrLogAdapter($log);
$adapter->log('test!', LOG_INFO);
$this->assertTrue($handler->hasInfoRecords());
$this->assertSame($log, $adapter->getLogObject());
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace Guzzle\Tests\Log;
use Guzzle\Log\Zf2LogAdapter;
use Zend\Log\Logger;
use Zend\Log\Writer\Stream;
/**
* @covers Guzzle\Log\Zf2LogAdapter
*/
class Zf2LogAdapterTest extends \Guzzle\Tests\GuzzleTestCase
{
/** @var Zf2LogAdapter */
protected $adapter;
/** @var Logger */
protected $log;
/** @var resource */
protected $stream;
protected function setUp()
{
$this->stream = fopen('php://temp', 'r+');
$this->log = new Logger();
$this->log->addWriter(new Stream($this->stream));
$this->adapter = new Zf2LogAdapter($this->log);
}
public function testLogsMessagesToAdaptedObject()
{
// Test without a priority
$this->adapter->log('Zend_Test!', \LOG_NOTICE);
rewind($this->stream);
$contents = stream_get_contents($this->stream);
$this->assertEquals(1, substr_count($contents, 'Zend_Test!'));
// Test with a priority
$this->adapter->log('Zend_Test!', \LOG_ALERT);
rewind($this->stream);
$contents = stream_get_contents($this->stream);
$this->assertEquals(2, substr_count($contents, 'Zend_Test!'));
}
public function testExposesAdaptedLogObject()
{
$this->assertEquals($this->log, $this->adapter->getLogObject());
}
}

View File

@ -0,0 +1,95 @@
<?php
namespace Guzzle\Tests\Plugin\Log;
use Guzzle\Log\ClosureLogAdapter;
use Guzzle\Http\Client;
use Guzzle\Http\EntityBody;
use Guzzle\Http\Message\Request;
use Guzzle\Http\Message\Response;
use Guzzle\Plugin\Log\LogPlugin;
use Guzzle\Common\Event;
/**
* @group server
* @covers Guzzle\Plugin\Log\LogPlugin
*/
class LogPluginTest extends \Guzzle\Tests\GuzzleTestCase
{
protected $adapter;
public function setUp()
{
$this->adapter = new ClosureLogAdapter(function ($message) {
echo $message;
});
}
public function testIgnoresCurlEventsWhenNotWiringBodies()
{
$p = new LogPlugin($this->adapter);
$this->assertNotEmpty($p->getSubscribedEvents());
$event = new Event(array('request' => new Request('GET', 'http://foo.com')));
$p->onCurlRead($event);
$p->onCurlWrite($event);
$p->onRequestBeforeSend($event);
}
public function testLogsWhenComplete()
{
$output = '';
$p = new LogPlugin(new ClosureLogAdapter(function ($message) use (&$output) {
$output = $message;
}), '{method} {resource} | {code} {res_body}');
$p->onRequestSent(new Event(array(
'request' => new Request('GET', 'http://foo.com'),
'response' => new Response(200, array(), 'Foo')
)));
$this->assertEquals('GET / | 200 Foo', $output);
}
public function testWiresBodiesWhenNeeded()
{
$client = new Client($this->getServer()->getUrl());
$plugin = new LogPlugin($this->adapter, '{req_body} | {res_body}', true);
$client->getEventDispatcher()->addSubscriber($plugin);
$request = $client->put();
// Send the response from the dummy server as the request body
$this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nsend");
$stream = fopen($this->getServer()->getUrl(), 'r');
$request->setBody(EntityBody::factory($stream, 4));
$tmpFile = tempnam(sys_get_temp_dir(), 'non_repeatable');
$request->setResponseBody(EntityBody::factory(fopen($tmpFile, 'w')));
$this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 8\r\n\r\nresponse");
ob_start();
$request->send();
$message = ob_get_clean();
unlink($tmpFile);
$this->assertContains("send", $message);
$this->assertContains("response", $message);
}
public function testHasHelpfulStaticFactoryMethod()
{
$s = fopen('php://temp', 'r+');
$client = new Client();
$client->addSubscriber(LogPlugin::getDebugPlugin(true, $s));
$request = $client->put('http://foo.com', array('Content-Type' => 'Foo'), 'Bar');
$request->setresponse(new Response(200), true);
$request->send();
rewind($s);
$contents = stream_get_contents($s);
$this->assertContains('# Request:', $contents);
$this->assertContainsIns('PUT / HTTP/1.1', $contents);
$this->assertContains('# Response:', $contents);
$this->assertContainsIns('HTTP/1.1 200 OK', $contents);
$this->assertContains('# Errors:', $contents);
}
}

View File

@ -0,0 +1,244 @@
<?php
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A TestListener that generates JSON messages.
*
* @since Class available since Release 3.0.0
*/
class PHPUnit_Util_Log_JSON extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener
{
/**
* @var string
*/
protected $currentTestSuiteName = '';
/**
* @var string
*/
protected $currentTestName = '';
/**
* @var bool
*/
protected $currentTestPass = true;
/**
* An error occurred.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param float $time
*/
public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
{
$this->writeCase(
'error',
$time,
PHPUnit_Util_Filter::getFilteredStacktrace($e, false),
$e->getMessage(),
$test
);
$this->currentTestPass = false;
}
/**
* A failure occurred.
*
* @param PHPUnit_Framework_Test $test
* @param PHPUnit_Framework_AssertionFailedError $e
* @param float $time
*/
public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
{
$this->writeCase(
'fail',
$time,
PHPUnit_Util_Filter::getFilteredStacktrace($e, false),
$e->getMessage(),
$test
);
$this->currentTestPass = false;
}
/**
* Incomplete test.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param float $time
*/
public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
{
$this->writeCase(
'error',
$time,
PHPUnit_Util_Filter::getFilteredStacktrace($e, false),
'Incomplete Test: ' . $e->getMessage(),
$test
);
$this->currentTestPass = false;
}
/**
* Risky test.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param float $time
*
* @since Method available since Release 4.0.0
*/
public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
{
$this->writeCase(
'error',
$time,
PHPUnit_Util_Filter::getFilteredStacktrace($e, false),
'Risky Test: ' . $e->getMessage(),
$test
);
$this->currentTestPass = false;
}
/**
* Skipped test.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param float $time
*/
public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
{
$this->writeCase(
'error',
$time,
PHPUnit_Util_Filter::getFilteredStacktrace($e, false),
'Skipped Test: ' . $e->getMessage(),
$test
);
$this->currentTestPass = false;
}
/**
* A testsuite started.
*
* @param PHPUnit_Framework_TestSuite $suite
*/
public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
{
$this->currentTestSuiteName = $suite->getName();
$this->currentTestName = '';
$this->write(
array(
'event' => 'suiteStart',
'suite' => $this->currentTestSuiteName,
'tests' => count($suite)
)
);
}
/**
* A testsuite ended.
*
* @param PHPUnit_Framework_TestSuite $suite
*/
public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
{
$this->currentTestSuiteName = '';
$this->currentTestName = '';
}
/**
* A test started.
*
* @param PHPUnit_Framework_Test $test
*/
public function startTest(PHPUnit_Framework_Test $test)
{
$this->currentTestName = PHPUnit_Util_Test::describe($test);
$this->currentTestPass = true;
$this->write(
array(
'event' => 'testStart',
'suite' => $this->currentTestSuiteName,
'test' => $this->currentTestName
)
);
}
/**
* A test ended.
*
* @param PHPUnit_Framework_Test $test
* @param float $time
*/
public function endTest(PHPUnit_Framework_Test $test, $time)
{
if ($this->currentTestPass) {
$this->writeCase('pass', $time, array(), '', $test);
}
}
/**
* @param string $status
* @param float $time
* @param array $trace
* @param string $message
* @param PHPUnit_Framework_TestCase|null $test
*/
protected function writeCase($status, $time, array $trace = array(), $message = '', $test = null)
{
$output = '';
// take care of TestSuite producing error (e.g. by running into exception) as TestSuite doesn't have hasOutput
if ($test !== null && method_exists($test, 'hasOutput') && $test->hasOutput()) {
$output = $test->getActualOutput();
}
$this->write(
array(
'event' => 'test',
'suite' => $this->currentTestSuiteName,
'test' => $this->currentTestName,
'status' => $status,
'time' => $time,
'trace' => $trace,
'message' => PHPUnit_Util_String::convertToUtf8($message),
'output' => $output,
)
);
}
/**
* @param string $buffer
*/
public function write($buffer)
{
array_walk_recursive($buffer, function (&$input) {
if (is_string($input)) {
$input = PHPUnit_Util_String::convertToUtf8($input);
}
});
$flags = 0;
if (defined('JSON_PRETTY_PRINT')) {
$flags |= JSON_PRETTY_PRINT;
}
parent::write(json_encode($buffer, $flags));
}
}

View File

@ -0,0 +1,459 @@
<?php
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A TestListener that generates a logfile of the test execution in XML markup.
*
* The XML markup used is the same as the one that is used by the JUnit Ant task.
*
* @since Class available since Release 2.1.0
*/
class PHPUnit_Util_Log_JUnit extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener
{
/**
* @var DOMDocument
*/
protected $document;
/**
* @var DOMElement
*/
protected $root;
/**
* @var bool
*/
protected $logIncompleteSkipped = false;
/**
* @var bool
*/
protected $writeDocument = true;
/**
* @var DOMElement[]
*/
protected $testSuites = array();
/**
* @var int[]
*/
protected $testSuiteTests = array(0);
/**
* @var int[]
*/
protected $testSuiteAssertions = array(0);
/**
* @var int[]
*/
protected $testSuiteErrors = array(0);
/**
* @var int[]
*/
protected $testSuiteFailures = array(0);
/**
* @var int[]
*/
protected $testSuiteTimes = array(0);
/**
* @var int
*/
protected $testSuiteLevel = 0;
/**
* @var DOMElement
*/
protected $currentTestCase = null;
/**
* @var bool
*/
protected $attachCurrentTestCase = true;
/**
* Constructor.
*
* @param mixed $out
* @param bool $logIncompleteSkipped
*/
public function __construct($out = null, $logIncompleteSkipped = false)
{
$this->document = new DOMDocument('1.0', 'UTF-8');
$this->document->formatOutput = true;
$this->root = $this->document->createElement('testsuites');
$this->document->appendChild($this->root);
parent::__construct($out);
$this->logIncompleteSkipped = $logIncompleteSkipped;
}
/**
* Flush buffer and close output.
*/
public function flush()
{
if ($this->writeDocument === true) {
$this->write($this->getXML());
}
parent::flush();
}
/**
* An error occurred.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param float $time
*/
public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
{
if ($this->currentTestCase === null) {
return;
}
if ($test instanceof PHPUnit_Framework_SelfDescribing) {
$buffer = $test->toString() . PHP_EOL;
} else {
$buffer = '';
}
if ($e instanceof PHPUnit_Framework_ExceptionWrapper) {
$type = $e->getClassname();
$buffer .= (string) $e;
} else {
$type = get_class($e);
$buffer .= PHPUnit_Framework_TestFailure::exceptionToString($e) . PHP_EOL .
PHPUnit_Util_Filter::getFilteredStacktrace($e);
}
$error = $this->document->createElement(
'error',
PHPUnit_Util_XML::prepareString($buffer)
);
$error->setAttribute('type', $type);
$this->currentTestCase->appendChild($error);
$this->testSuiteErrors[$this->testSuiteLevel]++;
}
/**
* A failure occurred.
*
* @param PHPUnit_Framework_Test $test
* @param PHPUnit_Framework_AssertionFailedError $e
* @param float $time
*/
public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
{
if ($this->currentTestCase === null) {
return;
}
if ($test instanceof PHPUnit_Framework_SelfDescribing) {
$buffer = $test->toString() . "\n";
} else {
$buffer = '';
}
$buffer .= PHPUnit_Framework_TestFailure::exceptionToString($e) .
"\n" .
PHPUnit_Util_Filter::getFilteredStacktrace($e);
$failure = $this->document->createElement(
'failure',
PHPUnit_Util_XML::prepareString($buffer)
);
$failure->setAttribute('type', get_class($e));
$this->currentTestCase->appendChild($failure);
$this->testSuiteFailures[$this->testSuiteLevel]++;
}
/**
* Incomplete test.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param float $time
*/
public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
{
if ($this->logIncompleteSkipped && $this->currentTestCase !== null) {
$error = $this->document->createElement(
'error',
PHPUnit_Util_XML::prepareString(
"Incomplete Test\n" .
PHPUnit_Util_Filter::getFilteredStacktrace($e)
)
);
$error->setAttribute('type', get_class($e));
$this->currentTestCase->appendChild($error);
$this->testSuiteErrors[$this->testSuiteLevel]++;
} else {
$this->attachCurrentTestCase = false;
}
}
/**
* Risky test.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param float $time
*
* @since Method available since Release 4.0.0
*/
public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
{
if ($this->logIncompleteSkipped && $this->currentTestCase !== null) {
$error = $this->document->createElement(
'error',
PHPUnit_Util_XML::prepareString(
"Risky Test\n" .
PHPUnit_Util_Filter::getFilteredStacktrace($e)
)
);
$error->setAttribute('type', get_class($e));
$this->currentTestCase->appendChild($error);
$this->testSuiteErrors[$this->testSuiteLevel]++;
} else {
$this->attachCurrentTestCase = false;
}
}
/**
* Skipped test.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param float $time
*
* @since Method available since Release 3.0.0
*/
public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
{
if ($this->logIncompleteSkipped && $this->currentTestCase !== null) {
$error = $this->document->createElement(
'error',
PHPUnit_Util_XML::prepareString(
"Skipped Test\n" .
PHPUnit_Util_Filter::getFilteredStacktrace($e)
)
);
$error->setAttribute('type', get_class($e));
$this->currentTestCase->appendChild($error);
$this->testSuiteErrors[$this->testSuiteLevel]++;
} else {
$this->attachCurrentTestCase = false;
}
}
/**
* A testsuite started.
*
* @param PHPUnit_Framework_TestSuite $suite
*
* @since Method available since Release 2.2.0
*/
public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
{
$testSuite = $this->document->createElement('testsuite');
$testSuite->setAttribute('name', $suite->getName());
if (class_exists($suite->getName(), false)) {
try {
$class = new ReflectionClass($suite->getName());
$testSuite->setAttribute('file', $class->getFileName());
} catch (ReflectionException $e) {
}
}
if ($this->testSuiteLevel > 0) {
$this->testSuites[$this->testSuiteLevel]->appendChild($testSuite);
} else {
$this->root->appendChild($testSuite);
}
$this->testSuiteLevel++;
$this->testSuites[$this->testSuiteLevel] = $testSuite;
$this->testSuiteTests[$this->testSuiteLevel] = 0;
$this->testSuiteAssertions[$this->testSuiteLevel] = 0;
$this->testSuiteErrors[$this->testSuiteLevel] = 0;
$this->testSuiteFailures[$this->testSuiteLevel] = 0;
$this->testSuiteTimes[$this->testSuiteLevel] = 0;
}
/**
* A testsuite ended.
*
* @param PHPUnit_Framework_TestSuite $suite
*
* @since Method available since Release 2.2.0
*/
public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
{
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'tests',
$this->testSuiteTests[$this->testSuiteLevel]
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'assertions',
$this->testSuiteAssertions[$this->testSuiteLevel]
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'failures',
$this->testSuiteFailures[$this->testSuiteLevel]
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'errors',
$this->testSuiteErrors[$this->testSuiteLevel]
);
$this->testSuites[$this->testSuiteLevel]->setAttribute(
'time',
sprintf('%F', $this->testSuiteTimes[$this->testSuiteLevel])
);
if ($this->testSuiteLevel > 1) {
$this->testSuiteTests[$this->testSuiteLevel - 1] += $this->testSuiteTests[$this->testSuiteLevel];
$this->testSuiteAssertions[$this->testSuiteLevel - 1] += $this->testSuiteAssertions[$this->testSuiteLevel];
$this->testSuiteErrors[$this->testSuiteLevel - 1] += $this->testSuiteErrors[$this->testSuiteLevel];
$this->testSuiteFailures[$this->testSuiteLevel - 1] += $this->testSuiteFailures[$this->testSuiteLevel];
$this->testSuiteTimes[$this->testSuiteLevel - 1] += $this->testSuiteTimes[$this->testSuiteLevel];
}
$this->testSuiteLevel--;
}
/**
* A test started.
*
* @param PHPUnit_Framework_Test $test
*/
public function startTest(PHPUnit_Framework_Test $test)
{
$testCase = $this->document->createElement('testcase');
$testCase->setAttribute('name', $test->getName());
if ($test instanceof PHPUnit_Framework_TestCase) {
$class = new ReflectionClass($test);
$methodName = $test->getName();
if ($class->hasMethod($methodName)) {
$method = $class->getMethod($test->getName());
$testCase->setAttribute('class', $class->getName());
$testCase->setAttribute('file', $class->getFileName());
$testCase->setAttribute('line', $method->getStartLine());
}
}
$this->currentTestCase = $testCase;
}
/**
* A test ended.
*
* @param PHPUnit_Framework_Test $test
* @param float $time
*/
public function endTest(PHPUnit_Framework_Test $test, $time)
{
if ($this->attachCurrentTestCase) {
if ($test instanceof PHPUnit_Framework_TestCase) {
$numAssertions = $test->getNumAssertions();
$this->testSuiteAssertions[$this->testSuiteLevel] += $numAssertions;
$this->currentTestCase->setAttribute(
'assertions',
$numAssertions
);
}
$this->currentTestCase->setAttribute(
'time',
sprintf('%F', $time)
);
$this->testSuites[$this->testSuiteLevel]->appendChild(
$this->currentTestCase
);
$this->testSuiteTests[$this->testSuiteLevel]++;
$this->testSuiteTimes[$this->testSuiteLevel] += $time;
if (method_exists($test, 'hasOutput') && $test->hasOutput()) {
$systemOut = $this->document->createElement('system-out');
$systemOut->appendChild(
$this->document->createTextNode($test->getActualOutput())
);
$this->currentTestCase->appendChild($systemOut);
}
}
$this->attachCurrentTestCase = true;
$this->currentTestCase = null;
}
/**
* Returns the XML as a string.
*
* @return string
*
* @since Method available since Release 2.2.0
*/
public function getXML()
{
return $this->document->saveXML();
}
/**
* Enables or disables the writing of the document
* in flush().
*
* This is a "hack" needed for the integration of
* PHPUnit with Phing.
*
* @return string
*
* @since Method available since Release 2.2.0
*/
public function setWriteDocument($flag)
{
if (is_bool($flag)) {
$this->writeDocument = $flag;
}
}
}

View File

@ -0,0 +1,257 @@
<?php
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A TestListener that generates a logfile of the
* test execution using the Test Anything Protocol (TAP).
*
* @since Class available since Release 3.0.0
*/
class PHPUnit_Util_Log_TAP extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener
{
/**
* @var int
*/
protected $testNumber = 0;
/**
* @var int
*/
protected $testSuiteLevel = 0;
/**
* @var bool
*/
protected $testSuccessful = true;
/**
* Constructor.
*
* @param mixed $out
*
* @throws PHPUnit_Framework_Exception
*
* @since Method available since Release 3.3.4
*/
public function __construct($out = null)
{
parent::__construct($out);
$this->write("TAP version 13\n");
}
/**
* An error occurred.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param float $time
*/
public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
{
$this->writeNotOk($test, 'Error');
}
/**
* A failure occurred.
*
* @param PHPUnit_Framework_Test $test
* @param PHPUnit_Framework_AssertionFailedError $e
* @param float $time
*/
public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
{
$this->writeNotOk($test, 'Failure');
$message = explode(
"\n",
PHPUnit_Framework_TestFailure::exceptionToString($e)
);
$diagnostic = array(
'message' => $message[0],
'severity' => 'fail'
);
if ($e instanceof PHPUnit_Framework_ExpectationFailedException) {
$cf = $e->getComparisonFailure();
if ($cf !== null) {
$diagnostic['data'] = array(
'got' => $cf->getActual(),
'expected' => $cf->getExpected()
);
}
}
$yaml = new Symfony\Component\Yaml\Dumper;
$this->write(
sprintf(
" ---\n%s ...\n",
$yaml->dump($diagnostic, 2, 2)
)
);
}
/**
* Incomplete test.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param float $time
*/
public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
{
$this->writeNotOk($test, '', 'TODO Incomplete Test');
}
/**
* Risky test.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param float $time
*
* @since Method available since Release 4.0.0
*/
public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
{
$this->write(
sprintf(
"ok %d - # RISKY%s\n",
$this->testNumber,
$e->getMessage() != '' ? ' ' . $e->getMessage() : ''
)
);
$this->testSuccessful = false;
}
/**
* Skipped test.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param float $time
*
* @since Method available since Release 3.0.0
*/
public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
{
$this->write(
sprintf(
"ok %d - # SKIP%s\n",
$this->testNumber,
$e->getMessage() != '' ? ' ' . $e->getMessage() : ''
)
);
$this->testSuccessful = false;
}
/**
* A testsuite started.
*
* @param PHPUnit_Framework_TestSuite $suite
*/
public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
{
$this->testSuiteLevel++;
}
/**
* A testsuite ended.
*
* @param PHPUnit_Framework_TestSuite $suite
*/
public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
{
$this->testSuiteLevel--;
if ($this->testSuiteLevel == 0) {
$this->write(sprintf("1..%d\n", $this->testNumber));
}
}
/**
* A test started.
*
* @param PHPUnit_Framework_Test $test
*/
public function startTest(PHPUnit_Framework_Test $test)
{
$this->testNumber++;
$this->testSuccessful = true;
}
/**
* A test ended.
*
* @param PHPUnit_Framework_Test $test
* @param float $time
*/
public function endTest(PHPUnit_Framework_Test $test, $time)
{
if ($this->testSuccessful === true) {
$this->write(
sprintf(
"ok %d - %s\n",
$this->testNumber,
PHPUnit_Util_Test::describe($test)
)
);
}
$this->writeDiagnostics($test);
}
/**
* @param PHPUnit_Framework_Test $test
* @param string $prefix
* @param string $directive
*/
protected function writeNotOk(PHPUnit_Framework_Test $test, $prefix = '', $directive = '')
{
$this->write(
sprintf(
"not ok %d - %s%s%s\n",
$this->testNumber,
$prefix != '' ? $prefix . ': ' : '',
PHPUnit_Util_Test::describe($test),
$directive != '' ? ' # ' . $directive : ''
)
);
$this->testSuccessful = false;
}
/**
* @param PHPUnit_Framework_Test $test
*/
private function writeDiagnostics(PHPUnit_Framework_Test $test)
{
if (!$test instanceof PHPUnit_Framework_TestCase) {
return;
}
if (!$test->hasOutput()) {
return;
}
foreach (explode("\n", trim($test->getActualOutput())) as $line) {
$this->write(
sprintf(
"# %s\n",
$line
)
);
}
}
}

View File

@ -0,0 +1,19 @@
Copyright (c) 2012 PHP Framework Interoperability Group
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,128 @@
<?php
namespace Psr\Log;
/**
* This is a simple Logger implementation that other Loggers can inherit from.
*
* It simply delegates all log-level-specific methods to the `log` method to
* reduce boilerplate code that a simple Logger that does the same thing with
* messages regardless of the error level has to implement.
*/
abstract class AbstractLogger implements LoggerInterface
{
/**
* System is unusable.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function emergency($message, array $context = array())
{
$this->log(LogLevel::EMERGENCY, $message, $context);
}
/**
* Action must be taken immediately.
*
* Example: Entire website down, database unavailable, etc. This should
* trigger the SMS alerts and wake you up.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function alert($message, array $context = array())
{
$this->log(LogLevel::ALERT, $message, $context);
}
/**
* Critical conditions.
*
* Example: Application component unavailable, unexpected exception.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function critical($message, array $context = array())
{
$this->log(LogLevel::CRITICAL, $message, $context);
}
/**
* Runtime errors that do not require immediate action but should typically
* be logged and monitored.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function error($message, array $context = array())
{
$this->log(LogLevel::ERROR, $message, $context);
}
/**
* Exceptional occurrences that are not errors.
*
* Example: Use of deprecated APIs, poor use of an API, undesirable things
* that are not necessarily wrong.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function warning($message, array $context = array())
{
$this->log(LogLevel::WARNING, $message, $context);
}
/**
* Normal but significant events.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function notice($message, array $context = array())
{
$this->log(LogLevel::NOTICE, $message, $context);
}
/**
* Interesting events.
*
* Example: User logs in, SQL logs.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function info($message, array $context = array())
{
$this->log(LogLevel::INFO, $message, $context);
}
/**
* Detailed debug information.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function debug($message, array $context = array())
{
$this->log(LogLevel::DEBUG, $message, $context);
}
}

View File

@ -0,0 +1,7 @@
<?php
namespace Psr\Log;
class InvalidArgumentException extends \InvalidArgumentException
{
}

View File

@ -0,0 +1,18 @@
<?php
namespace Psr\Log;
/**
* Describes log levels.
*/
class LogLevel
{
const EMERGENCY = 'emergency';
const ALERT = 'alert';
const CRITICAL = 'critical';
const ERROR = 'error';
const WARNING = 'warning';
const NOTICE = 'notice';
const INFO = 'info';
const DEBUG = 'debug';
}

View File

@ -0,0 +1,18 @@
<?php
namespace Psr\Log;
/**
* Describes a logger-aware instance.
*/
interface LoggerAwareInterface
{
/**
* Sets a logger instance on the object.
*
* @param LoggerInterface $logger
*
* @return void
*/
public function setLogger(LoggerInterface $logger);
}

View File

@ -0,0 +1,26 @@
<?php
namespace Psr\Log;
/**
* Basic Implementation of LoggerAwareInterface.
*/
trait LoggerAwareTrait
{
/**
* The logger instance.
*
* @var LoggerInterface
*/
protected $logger;
/**
* Sets a logger.
*
* @param LoggerInterface $logger
*/
public function setLogger(LoggerInterface $logger)
{
$this->logger = $logger;
}
}

View File

@ -0,0 +1,123 @@
<?php
namespace Psr\Log;
/**
* Describes a logger instance.
*
* The message MUST be a string or object implementing __toString().
*
* The message MAY contain placeholders in the form: {foo} where foo
* will be replaced by the context data in key "foo".
*
* The context array can contain arbitrary data. The only assumption that
* can be made by implementors is that if an Exception instance is given
* to produce a stack trace, it MUST be in a key named "exception".
*
* See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
* for the full interface specification.
*/
interface LoggerInterface
{
/**
* System is unusable.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function emergency($message, array $context = array());
/**
* Action must be taken immediately.
*
* Example: Entire website down, database unavailable, etc. This should
* trigger the SMS alerts and wake you up.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function alert($message, array $context = array());
/**
* Critical conditions.
*
* Example: Application component unavailable, unexpected exception.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function critical($message, array $context = array());
/**
* Runtime errors that do not require immediate action but should typically
* be logged and monitored.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function error($message, array $context = array());
/**
* Exceptional occurrences that are not errors.
*
* Example: Use of deprecated APIs, poor use of an API, undesirable things
* that are not necessarily wrong.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function warning($message, array $context = array());
/**
* Normal but significant events.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function notice($message, array $context = array());
/**
* Interesting events.
*
* Example: User logs in, SQL logs.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function info($message, array $context = array());
/**
* Detailed debug information.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function debug($message, array $context = array());
/**
* Logs with an arbitrary level.
*
* @param mixed $level
* @param string $message
* @param array $context
*
* @return void
*/
public function log($level, $message, array $context = array());
}

View File

@ -0,0 +1,140 @@
<?php
namespace Psr\Log;
/**
* This is a simple Logger trait that classes unable to extend AbstractLogger
* (because they extend another class, etc) can include.
*
* It simply delegates all log-level-specific methods to the `log` method to
* reduce boilerplate code that a simple Logger that does the same thing with
* messages regardless of the error level has to implement.
*/
trait LoggerTrait
{
/**
* System is unusable.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function emergency($message, array $context = array())
{
$this->log(LogLevel::EMERGENCY, $message, $context);
}
/**
* Action must be taken immediately.
*
* Example: Entire website down, database unavailable, etc. This should
* trigger the SMS alerts and wake you up.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function alert($message, array $context = array())
{
$this->log(LogLevel::ALERT, $message, $context);
}
/**
* Critical conditions.
*
* Example: Application component unavailable, unexpected exception.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function critical($message, array $context = array())
{
$this->log(LogLevel::CRITICAL, $message, $context);
}
/**
* Runtime errors that do not require immediate action but should typically
* be logged and monitored.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function error($message, array $context = array())
{
$this->log(LogLevel::ERROR, $message, $context);
}
/**
* Exceptional occurrences that are not errors.
*
* Example: Use of deprecated APIs, poor use of an API, undesirable things
* that are not necessarily wrong.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function warning($message, array $context = array())
{
$this->log(LogLevel::WARNING, $message, $context);
}
/**
* Normal but significant events.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function notice($message, array $context = array())
{
$this->log(LogLevel::NOTICE, $message, $context);
}
/**
* Interesting events.
*
* Example: User logs in, SQL logs.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function info($message, array $context = array())
{
$this->log(LogLevel::INFO, $message, $context);
}
/**
* Detailed debug information.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function debug($message, array $context = array())
{
$this->log(LogLevel::DEBUG, $message, $context);
}
/**
* Logs with an arbitrary level.
*
* @param mixed $level
* @param string $message
* @param array $context
*
* @return void
*/
abstract public function log($level, $message, array $context = array());
}

View File

@ -0,0 +1,28 @@
<?php
namespace Psr\Log;
/**
* This Logger can be used to avoid conditional log calls.
*
* Logging should always be optional, and if no logger is provided to your
* library creating a NullLogger instance to have something to throw logs at
* is a good way to avoid littering your code with `if ($this->logger) { }`
* blocks.
*/
class NullLogger extends AbstractLogger
{
/**
* Logs with an arbitrary level.
*
* @param mixed $level
* @param string $message
* @param array $context
*
* @return void
*/
public function log($level, $message, array $context = array())
{
// noop
}
}

View File

@ -0,0 +1,140 @@
<?php
namespace Psr\Log\Test;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
/**
* Provides a base test class for ensuring compliance with the LoggerInterface.
*
* Implementors can extend the class and implement abstract methods to run this
* as part of their test suite.
*/
abstract class LoggerInterfaceTest extends \PHPUnit_Framework_TestCase
{
/**
* @return LoggerInterface
*/
abstract public function getLogger();
/**
* This must return the log messages in order.
*
* The simple formatting of the messages is: "<LOG LEVEL> <MESSAGE>".
*
* Example ->error('Foo') would yield "error Foo".
*
* @return string[]
*/
abstract public function getLogs();
public function testImplements()
{
$this->assertInstanceOf('Psr\Log\LoggerInterface', $this->getLogger());
}
/**
* @dataProvider provideLevelsAndMessages
*/
public function testLogsAtAllLevels($level, $message)
{
$logger = $this->getLogger();
$logger->{$level}($message, array('user' => 'Bob'));
$logger->log($level, $message, array('user' => 'Bob'));
$expected = array(
$level.' message of level '.$level.' with context: Bob',
$level.' message of level '.$level.' with context: Bob',
);
$this->assertEquals($expected, $this->getLogs());
}
public function provideLevelsAndMessages()
{
return array(
LogLevel::EMERGENCY => array(LogLevel::EMERGENCY, 'message of level emergency with context: {user}'),
LogLevel::ALERT => array(LogLevel::ALERT, 'message of level alert with context: {user}'),
LogLevel::CRITICAL => array(LogLevel::CRITICAL, 'message of level critical with context: {user}'),
LogLevel::ERROR => array(LogLevel::ERROR, 'message of level error with context: {user}'),
LogLevel::WARNING => array(LogLevel::WARNING, 'message of level warning with context: {user}'),
LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of level notice with context: {user}'),
LogLevel::INFO => array(LogLevel::INFO, 'message of level info with context: {user}'),
LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of level debug with context: {user}'),
);
}
/**
* @expectedException \Psr\Log\InvalidArgumentException
*/
public function testThrowsOnInvalidLevel()
{
$logger = $this->getLogger();
$logger->log('invalid level', 'Foo');
}
public function testContextReplacement()
{
$logger = $this->getLogger();
$logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar'));
$expected = array('info {Message {nothing} Bob Bar a}');
$this->assertEquals($expected, $this->getLogs());
}
public function testObjectCastToString()
{
if (method_exists($this, 'createPartialMock')) {
$dummy = $this->createPartialMock('Psr\Log\Test\DummyTest', array('__toString'));
} else {
$dummy = $this->getMock('Psr\Log\Test\DummyTest', array('__toString'));
}
$dummy->expects($this->once())
->method('__toString')
->will($this->returnValue('DUMMY'));
$this->getLogger()->warning($dummy);
$expected = array('warning DUMMY');
$this->assertEquals($expected, $this->getLogs());
}
public function testContextCanContainAnything()
{
$context = array(
'bool' => true,
'null' => null,
'string' => 'Foo',
'int' => 0,
'float' => 0.5,
'nested' => array('with object' => new DummyTest),
'object' => new \DateTime,
'resource' => fopen('php://memory', 'r'),
);
$this->getLogger()->warning('Crazy context data', $context);
$expected = array('warning Crazy context data');
$this->assertEquals($expected, $this->getLogs());
}
public function testContextExceptionKeyCanBeExceptionOrOtherValues()
{
$logger = $this->getLogger();
$logger->warning('Random message', array('exception' => 'oops'));
$logger->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail')));
$expected = array(
'warning Random message',
'critical Uncaught Exception!'
);
$this->assertEquals($expected, $this->getLogs());
}
}
class DummyTest
{
public function __toString()
{
}
}

View File

@ -0,0 +1,45 @@
PSR Log
=======
This repository holds all interfaces/classes/traits related to
[PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md).
Note that this is not a logger of its own. It is merely an interface that
describes a logger. See the specification for more details.
Usage
-----
If you need a logger, you can use the interface like this:
```php
<?php
use Psr\Log\LoggerInterface;
class Foo
{
private $logger;
public function __construct(LoggerInterface $logger = null)
{
$this->logger = $logger;
}
public function doSomething()
{
if ($this->logger) {
$this->logger->info('Doing work');
}
// do something useful
}
}
```
You can then pick one of the implementations of the interface to get a logger.
If you want to implement the interface, you can require this package and
implement `Psr\Log\LoggerInterface` in your code. Please read the
[specification text](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md)
for details.

View File

@ -0,0 +1,26 @@
{
"name": "psr/log",
"description": "Common interface for logging libraries",
"keywords": ["psr", "psr-3", "log"],
"homepage": "https://github.com/php-fig/log",
"license": "MIT",
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"require": {
"php": ">=5.3.0"
},
"autoload": {
"psr-4": {
"Psr\\Log\\": "Psr/Log/"
}
},
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace Satooshi\Component\Log;
use Psr\Log\AbstractLogger;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Console logger for php-coveralls command.
*
* @author Kitamura Satoshi <with.no.parachute@gmail.com>
*/
class ConsoleLogger extends AbstractLogger
{
/**
* Output.
*
* @var \Symfony\Component\Console\Output\OutputInterface
*/
protected $output;
/**
* Constructor.
*
* @param OutputInterface $output
*/
public function __construct(OutputInterface $output)
{
$this->output = $output;
}
/**
* {@inheritdoc}
*
*
* @see \Psr\Log\LoggerInterface::log()
*/
public function log($level, $message, array $context = array())
{
$this->output->writeln($message);
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace Satooshi\Component\Log;
/**
* @covers Satooshi\Component\Log\ConsoleLogger
*
* @author Kitamura Satoshi <with.no.parachute@gmail.com>
*/
class ConsoleLoggerTest extends \PHPUnit_Framework_TestCase
{
protected function createAdapterMockWith($message)
{
$mock = $this->getMockBuilder('Symfony\Component\Console\Output\StreamOutput')
->disableOriginalConstructor()
->setMethods(array('writeln'))
->getMock();
$mock
->expects($this->once())
->method('writeln')
->with($this->equalTo($message));
return $mock;
}
/**
* @test
*/
public function shouldWritelnToOutput()
{
$message = 'log test message';
$output = $this->createAdapterMockWith($message);
$object = new ConsoleLogger($output);
$object->log('info', $message);
}
}

36
.gitignore vendored
View File

@ -13,25 +13,31 @@
# CUSTOM
######################################
.env
log/
run/
data/
# Keep folders
!.keepme
# Ignore variable dat
/.env
/log/[a-z0-9-]*
/data/
# Ignore custom MySQL configs
cfg/mysql-5.5/*.cnf
cfg/mysql-5.6/*.cnf
cfg/mysql-5.7/*.cnf
cfg/mariadb-5/*.cnf
cfg/mariadb-10/*.cnf
/cfg/mysql-5.5/*.cnf
/cfg/mysql-5.6/*.cnf
/cfg/mysql-5.7/*.cnf
/cfg/mysql-8.0/*.cnf
/cfg/mariadb-5/*.cnf
/cfg/mariadb-10.0/*.cnf
/cfg/mariadb-10.1/*.cnf
/cfg/mariadb-10.2/*.cnf
# Ignore custom PHP-FPM configs
cfg/php-fpm-5.4/*.ini
cfg/php-fpm-5.5/*.ini
cfg/php-fpm-5.6/*.ini
cfg/php-fpm-7.0/*.ini
cfg/php-fpm-7.1/*.ini
cfg/hhvm-latest/*.ini
/cfg/php-fpm-5.4/*.ini
/cfg/php-fpm-5.5/*.ini
/cfg/php-fpm-5.6/*.ini
/cfg/php-fpm-7.0/*.ini
/cfg/php-fpm-7.1/*.ini
/cfg/hhvm-latest/*.ini

0
log/.keepme Normal file
View File