<?php
Phar::mapPhar("PhpConsole");
spl_autoload_register(function ($class) {
	if(strpos($class, "PhpConsole") === 0) {
		require_once("phar://". str_replace("\\", DIRECTORY_SEPARATOR, $class) . ".php");
	}
});
__HALT_COMPILER(); ?>
3                    Connector.phpG  r TG  к         EvalProvider.php  r T  G         OldVersionAdapter.phpK  r TK  g0hX         Handler.php<  r T<  PY         Dispatcher.php-  r T-  Ģ9         Storage.php~  r T~  dɶ         Dispatcher/Evaluate.php  r T  8͡         Dispatcher/Debug.php  r T  t4;         Dispatcher/Errors.php  r T  &)ȶ      
   Helper.php  r T  J"WA      
   Dumper.php  r T           Storage/Session.php  r T  P         Storage/Memcache.php  r T  G(e         Storage/File.php{
  r T{
  @e         Storage/ExpiringKeyValue.php)  r T)  (1         Storage/AllKeysList.php  r T  ٺ         PsrLogger.php	  r T	  yF         Auth.php
  r T
  R      <?php

namespace PhpConsole;

/**
 * PHP Console client connector that encapsulates client-server protocol implementation
 *
 * You will need to install Google Chrome extension "PHP Console"
 * https://chrome.google.com/webstore/detail/php-console/nfhmhhlpfleoednkpnnnkolmclajemef
 *
 * @package PhpConsole
 * @version 3.1
 * @link http://php-console.com
 * @author Sergey Barbushin http://linkedin.com/in/barbushin
 * @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
 * @license http://www.opensource.org/licenses/BSD-3-Clause "The BSD 3-Clause License"
 */
class Connector {

	const SERVER_PROTOCOL = 5;
	const SERVER_COOKIE = 'php-console-server';
	const CLIENT_INFO_COOKIE = 'php-console-client';
	const CLIENT_ENCODING = 'UTF-8';
	const HEADER_NAME = 'PHP-Console';
	const POSTPONE_HEADER_NAME = 'PHP-Console-Postpone';
	const POST_VAR_NAME = '__PHP_Console';
	const POSTPONE_REQUESTS_LIMIT = 10;
	const PHP_HEADERS_SIZE = 1000; // maximum PHP response headers size
	const CLIENT_HEADERS_LIMIT = 200000;

	/** @var Connector */
	protected static $instance;
	/** @var  Storage|null */
	private static $postponeStorage;

	/** @var  Dumper|null */
	protected $dumper;
	/** @var  Dispatcher\Debug|null */
	protected $debugDispatcher;
	/** @var  Dispatcher\Errors|null */
	protected $errorsDispatcher;
	/** @var  Dispatcher\Evaluate|null */
	protected $evalDispatcher;
	/** @var  string */
	protected $serverEncoding = self::CLIENT_ENCODING;
	protected $sourcesBasePath;
	protected $headersLimit;

	/** @var Client|null */
	private $client;
	/** @var Auth|null */
	private $auth;
	/** @var Message[] */
	private $messages = array();
	private $postponeResponseId;
	private $isSslOnlyMode = false;
	private $isActiveClient = false;
	private $isAuthorized = false;
	private $isEvalListenerStarted = false;
	private $registeredShutDowns = 0;

	/**
	 * @return static
	 */
	public static function getInstance() {
		if(!self::$instance) {
			self::$instance = new static();
		}
		return self::$instance;
	}

	/**
	 * Set storage for postponed response data. Storage\Session is used by default, but if you have problems with overridden session handler you should use another one.
	 * IMPORTANT: This method cannot be called after Connector::getInstance()
	 * @param Storage $storage
	 * @throws \Exception
	 */
	public static function setPostponeStorage(Storage $storage) {
		if(self::$instance) {
			throw new \Exception(__METHOD__ . ' can be called only before ' . __CLASS__ . '::getInstance()');
		}
		self::$postponeStorage = $storage;
	}

	/**
	 * @return Storage
	 */
	private final function getPostponeStorage() {
		if(!self::$postponeStorage) {
			self::$postponeStorage = new Storage\Session();
		}
		return self::$postponeStorage;
	}

	protected function __construct() {
		$this->initConnection();
		$this->setServerEncoding(ini_get('mbstring.internal_encoding') ? : self::CLIENT_ENCODING);
	}

	private final function __clone() {
	}

	/**
	 * Detect script is running in command-line mode
	 * @return int
	 */
	protected function isCliMode() {
		return PHP_SAPI == 'cli';
	}

	/**
	 * Notify clients that there is active PHP Console on server & check if there is request from client with active PHP Console
	 * @throws \Exception
	 */
	private final function initConnection() {
		if($this->isCliMode()) {
			return;
		}

		$this->initServerCookie();
		$this->client = $this->initClient();

		if($this->client) {
			ob_start();
			$this->isActiveClient = true;
			$this->registerFlushOnShutDown();
			$this->setHeadersLimit(isset($_SERVER['SERVER_SOFTWARE']) && stripos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false
				? 4096 // default headers limit for Nginx
				: 8192 // default headers limit for all other web-servers
			);
			$this->listenGetPostponedResponse();
			$this->postponeResponseId = $this->setPostponeHeader();
		}
	}

	/**
	 * Get connected client data(
	 * @return Client|null
	 * @throws \Exception
	 */
	private final function initClient() {
		if(isset($_COOKIE[self::CLIENT_INFO_COOKIE])) {
			$clientData = @json_decode(base64_decode($_COOKIE[self::CLIENT_INFO_COOKIE], true), true);
			if(!$clientData) {
				throw new \Exception('Wrong format of response cookie data: ' . $_COOKIE[self::CLIENT_INFO_COOKIE]);
			}

			$client = new Client($clientData);
			if(isset($clientData['auth'])) {
				$client->auth = new ClientAuth($clientData['auth']);
			}
			return $client;
		}
	}

	/**
	 * Notify clients that there is active PHP Console on server
	 * @throws \Exception
	 */
	private final function initServerCookie() {
		if(!isset($_COOKIE[self::SERVER_COOKIE]) || $_COOKIE[self::SERVER_COOKIE] != self::SERVER_PROTOCOL) {
			$isSuccess = setcookie(self::SERVER_COOKIE, self::SERVER_PROTOCOL, null, '/');
			if(!$isSuccess) {
				throw new \Exception('Unable to set PHP Console server cookie');
			}
		}
	}

	/**
	 * Check if there is client is installed PHP Console extension
	 * @return bool
	 */
	public function isActiveClient() {
		return $this->isActiveClient;
	}

	/**
	 * Set client connection as not active
	 */
	protected function breakClientConnection() {
		$this->isActiveClient = false;
	}

	/**
	 * Check if client with valid auth credentials is connected
	 * @return bool
	 */
	public function isAuthorized() {
		return $this->isAuthorized;
	}

	/**
	 * Set IP masks of clients that will be allowed to connect to PHP Console
	 * @param array $ipMasks Use *(star character) for "any numbers" placeholder array('192.168.*.*', '10.2.12*.*', '127.0.0.1')
	 */
	public function setAllowedIpMasks(array $ipMasks) {
		if($this->isActiveClient()) {
			if(isset($_SERVER['REMOTE_ADDR'])) {
				$ip = $_SERVER['REMOTE_ADDR'];
				foreach($ipMasks as $ipMask) {
					if(preg_match('~^' . str_replace(array('.', '*'), array('\.', '\w+'), $ipMask) . '$~i', $ip)) {
						return;
					}
				}
			}
			$this->breakClientConnection();
		}
	}

	/**
	 * @return Dumper
	 */
	public function getDumper() {
		if(!$this->dumper) {
			$this->dumper = new Dumper();
		}
		return $this->dumper;
	}

	/**
	 * Override default errors dispatcher
	 * @param Dispatcher\Errors $dispatcher
	 */
	public function setErrorsDispatcher(Dispatcher\Errors $dispatcher) {
		$this->errorsDispatcher = $dispatcher;
	}

	/**
	 * Get dispatcher responsible for sending errors/exceptions messages
	 * @return Dispatcher\Errors
	 */
	public function getErrorsDispatcher() {
		if(!$this->errorsDispatcher) {
			$this->errorsDispatcher = new Dispatcher\Errors($this, $this->getDumper());
		}
		return $this->errorsDispatcher;
	}

	/**
	 * Override default debug dispatcher
	 * @param Dispatcher\Debug $dispatcher
	 */
	public function setDebugDispatcher(Dispatcher\Debug $dispatcher) {
		$this->debugDispatcher = $dispatcher;
	}

	/**
	 * Get dispatcher responsible for sending debug messages
	 * @return Dispatcher\Debug
	 */
	public function getDebugDispatcher() {
		if(!$this->debugDispatcher) {
			$this->debugDispatcher = new Dispatcher\Debug($this, $this->getDumper());
		}
		return $this->debugDispatcher;
	}

	/**
	 * Override default eval requests dispatcher
	 * @param Dispatcher\Evaluate $dispatcher
	 */
	public function setEvalDispatcher(Dispatcher\Evaluate $dispatcher) {
		$this->evalDispatcher = $dispatcher;
	}

	/**
	 * Get dispatcher responsible for handling eval requests
	 * @return Dispatcher\Evaluate
	 */
	public function getEvalDispatcher() {
		if(!$this->evalDispatcher) {
			$this->evalDispatcher = new Dispatcher\Evaluate($this, new EvalProvider(), $this->getDumper());
		}
		return $this->evalDispatcher;
	}

	/**
	 * Enable eval request to be handled by eval dispatcher. Must be called after all Connector configurations.
	 * Connector::getInstance()->setPassword() is required to be called before this method
	 * Use Connector::getInstance()->setAllowedIpMasks() for additional access protection
	 * Check Connector::getInstance()->getEvalDispatcher()->getEvalProvider() to customize eval accessibility & security options
	 * @param bool $exitOnEval
	 * @param bool $flushDebugMessages Clear debug messages handled before this method is called
	 * @throws \Exception
	 */
	public final function startEvalRequestsListener($exitOnEval = true, $flushDebugMessages = true) {
		if(!$this->auth) {
			throw new \Exception('Eval dispatcher is allowed only in password protected mode. See PhpConsole\Connector::getInstance()->setPassword(...)');
		}
		if($this->isEvalListenerStarted) {
			throw new \Exception('Eval requests listener already started');
		}
		$this->isEvalListenerStarted = true;

		if($this->isActiveClient() && $this->isAuthorized() && isset($_POST[Connector::POST_VAR_NAME]['eval'])) {
			$request = $_POST[Connector::POST_VAR_NAME]['eval'];
			if(!isset($request['data']) || !isset($request['signature'])) {
				throw new \Exception('Wrong PHP Console eval request');
			}
			if($this->auth->getSignature($request['data']) !== $request['signature']) {
				throw new \Exception('Wrong PHP Console eval request signature');
			}
			if($flushDebugMessages) {
				foreach($this->messages as $i => $message) {
					if($message instanceof DebugMessage) {
						unset($this->messages[$i]);
					}
				}
			}
			$this->convertEncoding($request['data'], $this->serverEncoding, self::CLIENT_ENCODING);
			$this->getEvalDispatcher()->dispatchCode($request['data']);
			if($exitOnEval) {
				exit;
			}
		}
	}

	/**
	 * Set bath to base dir of project source code(so it will be stripped in paths displaying on client)
	 * @param $sourcesBasePath
	 * @throws \Exception
	 */
	public final function setSourcesBasePath($sourcesBasePath) {
		$sourcesBasePath = realpath($sourcesBasePath);
		if(!$sourcesBasePath) {
			throw new \Exception('Path "' . $sourcesBasePath . '" not found');
		}
		$this->sourcesBasePath = $sourcesBasePath;
	}

	/**
	 * Protect PHP Console connection by password
	 *
	 * Use Connector::getInstance()->setAllowedIpMasks() for additional secure
	 * @param string $password
	 * @param bool $publicKeyByIp Set authorization token depending on client IP
	 * @throws \Exception
	 */
	public final function setPassword($password, $publicKeyByIp = true) {
		if($this->auth) {
			throw new \Exception('Password already defined');
		}
		$this->convertEncoding($password, self::CLIENT_ENCODING, $this->serverEncoding);
		$this->auth = new Auth($password, $publicKeyByIp);
		if($this->client) {
			$this->isAuthorized = $this->client->auth && $this->auth->isValidAuth($this->client->auth);
		}
	}

	/**
	 * Encode var to JSON with errors & encoding handling
	 * @param $var
	 * @return string
	 * @throws \Exception
	 */
	protected function jsonEncode($var) {
		return json_encode($var, defined('JSON_UNESCAPED_UNICODE') ? JSON_UNESCAPED_UNICODE : null);
	}

	/**
	 * Recursive var data encoding conversion
	 * @param $data
	 * @param $fromEncoding
	 * @param $toEncoding
	 */
	protected function convertArrayEncoding(&$data, $toEncoding, $fromEncoding) {
		array_walk_recursive($data, array($this, 'convertWalkRecursiveItemEncoding'), array($toEncoding, $fromEncoding));
	}

	/**
	 * Encoding conversion callback for array_walk_recursive()
	 * @param string $string
	 * @param null $key
	 * @param array $args
	 */
	protected function convertWalkRecursiveItemEncoding(&$string, $key = null, array $args) {
		$this->convertEncoding($string, $args[0], $args[1]);
	}

	/**
	 * Convert string encoding
	 * @param string $string
	 * @param string $toEncoding
	 * @param string|null $fromEncoding
	 * @throws \Exception
	 */
	protected function convertEncoding(&$string, $toEncoding, $fromEncoding) {
		if($string && is_string($string) && $toEncoding != $fromEncoding) {
			static $isMbString;
			if($isMbString === null) {
				$isMbString = extension_loaded('mbstring');
			}
			if($isMbString) {
				$string = @mb_convert_encoding($string, $toEncoding, $fromEncoding) ? : $string;
			}
			else {
				$string = @iconv($fromEncoding, $toEncoding . '//IGNORE', $string) ? : $string;
			}
			if(!$string && $toEncoding == 'UTF-8') {
				$string = utf8_encode($string);
			}
		}
	}

	/**
	 * Set headers size limit for your web-server. You can auto-detect headers size limit by /examples/utils/detect_headers_limit.php
	 * @param $bytes
	 * @throws \Exception
	 */
	public final function setHeadersLimit($bytes) {
		if($bytes < static::PHP_HEADERS_SIZE) {
			throw new \Exception('Headers limit cannot be less then ' . __CLASS__ . '::PHP_HEADERS_SIZE');
		}
		$bytes -= static::PHP_HEADERS_SIZE;
		$this->headersLimit = $bytes < static::CLIENT_HEADERS_LIMIT ? $bytes : static::CLIENT_HEADERS_LIMIT;
	}

	/**
	 * Set your server PHP internal encoding, if it's different from "mbstring.internal_encoding" or UTF-8
	 * @param $encoding
	 */
	public final function setServerEncoding($encoding) {
		if($encoding == 'utf8' || $encoding == 'utf-8') {
			$encoding = 'UTF-8'; // otherwise mb_convert_encoding() sometime fails with error(thanks to @alexborisov)
		}
		$this->serverEncoding = $encoding;
	}

	/**
	 * Send data message to PHP Console client(if it's connected)
	 * @param Message $message
	 */
	public function sendMessage(Message $message) {
		if($this->isActiveClient()) {
			$this->messages[] = $message;
		}
	}

	/**
	 * Register shut down callback handler. Must be called after all errors handlers register_shutdown_function()
	 */
	public final function registerFlushOnShutDown() {
		$this->registeredShutDowns++;
		register_shutdown_function(array($this, 'onShutDown'));
	}

	/**
	 * This method must be called only by register_shutdown_function(). Never call it manually!
	 */
	public function onShutDown() {
		$this->registeredShutDowns--;
		if(!$this->registeredShutDowns) {
			$this->proceedResponsePackage();
		}
	}

	/**
	 * Force connection by SSL for clients with PHP Console installed
	 */
	public function enableSslOnlyMode() {
		$this->isSslOnlyMode = true;
	}

	/**
	 * Check if client is connected by SSL
	 * @return bool
	 */
	protected function isSsl() {
		return (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') || (isset($_SERVER['SERVER_PORT']) && ($_SERVER['SERVER_PORT']) == 443);
	}

	/**
	 * Send response data to client
	 * @throws \Exception
	 */
	private final function proceedResponsePackage() {
		if($this->isActiveClient()) {
			$response = new Response();
			$response->isSslOnlyMode = $this->isSslOnlyMode;

			if(isset($_POST[self::POST_VAR_NAME]['getBackData'])) {
				$response->getBackData = $_POST[self::POST_VAR_NAME]['getBackData'];
			}

			if(!$this->isSslOnlyMode || $this->isSsl()) {
				if($this->auth) {
					$response->auth = $this->auth->getServerAuthStatus($this->client->auth);
				}
				if(!$this->auth || $this->isAuthorized()) {
					$response->isLocal = isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] == '127.0.0.1';
					$response->docRoot = isset($_SERVER['DOCUMENT_ROOT']) ? $_SERVER['DOCUMENT_ROOT'] : null;
					$response->sourcesBasePath = $this->sourcesBasePath;
					$response->isEvalEnabled = $this->isEvalListenerStarted;
					$response->messages = $this->messages;
				}
			}

			$responseData = $this->serializeResponse($response);

			if(strlen($responseData) > $this->headersLimit || !$this->setHeaderData($responseData, self::HEADER_NAME, false)) {
				$this->getPostponeStorage()->push($this->postponeResponseId, $responseData);
			}
		}
	}

	private final function setPostponeHeader() {
		$postponeResponseId = mt_rand() . mt_rand() . mt_rand();
		$this->setHeaderData($this->serializeResponse(
			new PostponedResponse(array(
				'id' => $postponeResponseId
			))
		), self::POSTPONE_HEADER_NAME, true);
		return $postponeResponseId;
	}

	private final function setHeaderData($responseData, $headerName, $throwException = true) {
		if(headers_sent($file, $line)) {
			if($throwException) {
				throw new \Exception('Unable to process response data, headers already sent in ' . $file . ':' . $line . '. Try to use ob_start() and don\'t use flush().');
			}
			return false;
		}
		header($headerName . ': ' . $responseData);
		return true;
	}

	protected function objectToArray(&$var) {
		if(is_object($var)) {
			$var = get_object_vars($var);
			array_walk_recursive($var, array($this, 'objectToArray'));
		}
	}

	protected function serializeResponse(DataObject $response) {
		if($this->serverEncoding != self::CLIENT_ENCODING) {
			$this->objectToArray($response);
			$this->convertArrayEncoding($response, self::CLIENT_ENCODING, $this->serverEncoding);
		}
		return $this->jsonEncode($response);
	}

	/**
	 * Check if there is postponed response request and dispatch it
	 */
	private final function listenGetPostponedResponse() {
		if(isset($_POST[self::POST_VAR_NAME]['getPostponedResponse'])) {
			header('Content-Type: application/json; charset=' . self::CLIENT_ENCODING);
			echo $this->getPostponeStorage()->pop($_POST[self::POST_VAR_NAME]['getPostponedResponse']);
			$this->breakClientConnection();
			exit;
		}
	}
}

abstract class DataObject {

	public function __construct(array $properties = array()) {
		foreach($properties as $property => $value) {
			$this->$property = $value;
		}
	}
}

final class Client extends DataObject {

	public $protocol;
	/** @var ClientAuth|null */
	public $auth;
}

final class ClientAuth extends DataObject {

	public $publicKey;
	public $token;
}

final class ServerAuthStatus extends DataObject {

	public $publicKey;
	public $isSuccess;
}

final class Response extends DataObject {

	public $protocol = Connector::SERVER_PROTOCOL;
	/** @var  ServerAuthStatus */
	public $auth;
	public $docRoot;
	public $sourcesBasePath;
	public $getBackData;
	public $isLocal;
	public $isSslOnlyMode;
	public $isEvalEnabled;
	public $messages = array();
}

final class PostponedResponse extends DataObject {

	public $protocol = Connector::SERVER_PROTOCOL;
	public $isPostponed = true;
	public $id;
}

abstract class Message extends DataObject {

	public $type;
}

abstract class EventMessage extends Message {

	public $data;
	public $file;
	public $line;
	/** @var  null|TraceCall[] */
	public $trace;
}

final class TraceCall extends DataObject {

	public $file;
	public $line;
	public $call;
}

final class DebugMessage extends EventMessage {

	public $type = 'debug';
	public $tags;
}

final class ErrorMessage extends EventMessage {

	public $type = 'error';
	public $code;
	public $class;
}

final class EvalResultMessage extends Message {

	public $type = 'eval_result';
	public $return;
	public $output;
	public $time;
}

<?php

namespace PhpConsole;

/**
 * Execute PHP code with some security & accessibility tweaks
 *
 * @package PhpConsole
 * @version 3.1
 * @link http://php-console.com
 * @author Sergey Barbushin http://linkedin.com/in/barbushin
 * @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
 * @license http://www.opensource.org/licenses/BSD-3-Clause "The BSD 3-Clause License"
 */
class EvalProvider {

	protected $sharedVars = array();
	protected $openBaseDirs = array();
	protected $codeCallbackHandlers = array();
	protected $globalsBackup;

	/**
	 * Execute PHP code handling execution time, output & exception
	 * @param string $code
	 * @return EvalResult
	 */
	public function evaluate($code) {
		$code = $this->applyHandlersToCode($code);
		$code = $this->adaptCodeToEval($code);

		$this->backupGlobals();
		$this->applyOpenBaseDirSetting();

		$startTime = microtime(true);
		static::executeCode('', $this->sharedVars);
		$selfTime = microtime(true) - $startTime;

		ob_start();
		$result = new EvalResult();
		$startTime = microtime(true);
		try {
			$result->return = static::executeCode($code, $this->sharedVars);
		}
		catch(\Exception $exception) {
			$result->exception = $exception;
		}
		$result->time = abs(microtime(true) - $startTime - $selfTime);
		$result->output = ob_get_clean();

		$this->restoreGlobals();

		return $result;
	}

	/**
	 * Add callback that will be called with &$code var reference before code execution
	 * @param $callback
	 * @throws \Exception
	 */
	public function addCodeHandler($callback) {
		if(!is_callable($callback)) {
			throw new \Exception('Argument is not callable');
		}
		$this->codeCallbackHandlers[] = $callback;
	}

	/**
	 * Call added code handlers
	 * @param $code
	 * @return mixed
	 */
	protected function applyHandlersToCode($code) {
		foreach($this->codeCallbackHandlers as $callback) {
			call_user_func_array($callback, array(&$code));
		}
		return $code;
	}

	/**
	 * Store global vars data in backup var
	 */
	protected function backupGlobals() {
		$this->globalsBackup = array();
		foreach($GLOBALS as $key => $value) {
			if($key != 'GLOBALS') {
				$this->globalsBackup[$key] = $value;
			}
		}
	}

	/**
	 * Restore global vars data from backup var
	 */
	protected function restoreGlobals() {
		foreach($this->globalsBackup as $key => $value) {
			$GLOBALS[$key] = $value;
		}
		foreach(array_diff(array_keys($GLOBALS), array_keys($this->globalsBackup)) as $newKey) {
			if($newKey != 'GLOBALS') {
				unset($GLOBALS[$newKey]);
			}
		}
	}

	/**
	 * Execute code with shared vars
	 * @param $code
	 * @param array $sharedVars
	 * @return mixed
	 */
	protected static function executeCode($code, array $sharedVars) {
		unset($code);
		unset($sharedVars);

		foreach(func_get_arg(1) as $var => $value) {
			if(isset($GLOBALS[$var]) && $var[0] == '_') { // extract($this->sharedVars, EXTR_OVERWRITE) and $$var = $value do not overwrites global vars
				$GLOBALS[$var] = $value;
			}
			else {
				$$var = $value;
			}
		}

		return eval(func_get_arg(0));
	}

	/**
	 * Prepare code PHP tags be correctly passed to eval() function
	 * @param string $code
	 * @return string
	 */
	protected function trimPhpTags($code) {
		$replace = array(
			'~^(\s*)<\?=~s' => '\1echo ',
			'~^(\s*)<\?(php)?~is' => '\1',
			'~\?>\s*$~s' => '',
			'~<\?(php)?[\s;]*$~is' => '',
		);
		return preg_replace(array_keys($replace), $replace, $code);
	}

	/**
	 * Add semicolon to the end of code if it's required
	 * @param string $code
	 * @return string
	 */
	protected function forceEndingSemicolon($code) {
		$code = rtrim($code, "; \r\n");
		return $code[strlen($code) - 1] != '}' ? $code . ';' : $code;
	}

	/**
	 * Apply some default code handlers
	 * @param string $code
	 * @return string
	 */
	protected function adaptCodeToEval($code) {
		$code = $this->trimPhpTags($code);
		$code = $this->forceEndingSemicolon($code);
		return $code;
	}

	/**
	 * Protect response code access only to specified directories using http://www.php.net/manual/en/ini.core.php#ini.open-basedir
	 * IMPORTANT: classes autoload methods will work only for specified directories
	 * @param array $openBaseDirs
	 */
	public function setOpenBaseDirs(array $openBaseDirs) {
		$this->openBaseDirs = $openBaseDirs;
	}

	/**
	 * Autoload all PHP Console classes
	 */
	protected function forcePhpConsoleClassesAutoLoad() {
		foreach(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(__DIR__), \RecursiveIteratorIterator::LEAVES_ONLY) as $path) {
			/** @var $path \SplFileInfo */
			if($path->isFile() && $path->getExtension() == 'php' && $path->getFilename() !== 'PsrLogger.php') {
				require_once($path->getPathname());
			}
		}
	}

	/**
	 * Set actual "open_basedir" PHP ini option
	 * @throws \Exception
	 */
	protected function applyOpenBaseDirSetting() {
		if($this->openBaseDirs) {
			$value = implode(PATH_SEPARATOR, $this->openBaseDirs);
			if(ini_get('open_basedir') != $value) {
				$this->forcePhpConsoleClassesAutoLoad();
				if(ini_set('open_basedir', $value) === false) {
					throw new \Exception('Unable to set "open_basedir" php.ini setting');
				}
			}
		}
	}

	/**
	 * Protect response code from reading/writing/including any files using http://www.php.net/manual/en/ini.core.php#ini.open-basedir
	 * IMPORTANT: It does not protects from system(), exec(), passthru(), popen() & etc OS commands execution functions
	 * IMPORTANT: Classes autoload methods will not work, so all required classes must be loaded before code evaluation
	 */
	public function disableFileAccessByOpenBaseDir() {
		$this->setOpenBaseDirs(array(__DIR__ . '/not_existed_dir' . mt_rand()));
	}

	/**
	 * Add var that will be implemented in PHP code executed from PHP Console debug panel (will be implemented in PHP Console > v3.0)
	 * @param $name
	 * @param $var
	 * @throws \Exception
	 * @internal param bool $asReference
	 */
	public function addSharedVar($name, $var) {
		$this->addSharedVarReference($name, $var);
	}

	/**
	 * Add var that will be implemented in PHP code executed from PHP Console debug panel (will be implemented in PHP Console > v3.0)
	 * @param $name
	 * @param $var
	 * @throws \Exception
	 * @internal param bool $asReference
	 */
	public function addSharedVarReference($name, &$var) {
		if(isset($this->sharedVars[$name])) {
			throw new \Exception('Var with name "' . $name . '" already added');
		}
		$this->sharedVars[$name] =& $var;
	}
}

class EvalResult {

	public $return;
	public $output;
	public $time;
	/** @var  \Exception|null */
	public $exception;
}
<?php

namespace PhpConsole {

	/**
	 * There is adapter of PhpConsole v1.x to v3.x
	 * It's for users that just want to migrate from PhpConsole v1 to v3 without any code changes
	 *
	 * Usage:
	 *
	 * 1. Register PhpConsole class emulator
	 *
	 * require_once('/path/to/src/PhpConsole/__autoload.php');
	 * \PhpConsole\OldVersionAdapter::register();
	 *
	 * 2. Call PhpConsole v1.x methods as is:
	 *
	 * $pc = PhpConsole::getInstance();
	 * $pc->start($handleErrors = true, $handleExceptions = true, $sourceBasePath = null);
	 * PhpConsole::debug('message', 'some,tags');
	 * debug('message', 'some,tags');
	 *
	 * IMPORTANT: This adapter will be removed in PhpConsole > v3, so it's strongly recommended to migrate your code using original PhpConsole v3 methods
	 *
	 * @package PhpConsole
	 * @version 3.1
	 * @link http://php-console.com
	 * @author Sergey Barbushin http://linkedin.com/in/barbushin
	 * @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
	 * @license http://www.opensource.org/licenses/BSD-3-Clause "The BSD 3-Clause License"
	 */
	class OldVersionAdapter {

		public static $callOldErrorHandler = true;
		public static $callOldExceptionsHandler = true;

		/** @var OldVersionAdapter|null */
		protected static $instance;

		private function __construct() {
		}

		/**
		 * This method must be called just to force \PhpConsole class initialization
		 * @param Connector|null $connector
		 * @param Handler|null $handler
		 * @throws \Exception
		 * @return Connector
		 */
		public static function register(Connector $connector = null, Handler $handler = null) {
		}

		/**
		 * Start PhpConsole v1 handler
		 * @param bool $handleErrors
		 * @param bool $handleExceptions
		 * @param null|string $sourceBasePath
		 */
		public static function start($handleErrors = true, $handleExceptions = true, $sourceBasePath = null) {
			if(self::$instance) {
				die('PhpConsole already started');
			}
			self::$instance = new static();

			$handler = Handler::getInstance();
			$handler->setHandleErrors($handleErrors);
			$handler->setHandleExceptions($handleExceptions);
			$handler->setCallOldHandlers(self::$callOldErrorHandler || self::$callOldExceptionsHandler);
			$handler->start();

			$connector = $handler->getConnector();
			$connector->setSourcesBasePath($sourceBasePath);
		}

		public static function getInstance() {
			if(!self::$instance) {
				throw new \Exception('PhpConsole not started');
			}
			return self::$instance;
		}

		/**
		 * @return Connector
		 */
		public function getConnector() {
			return Connector::getInstance();
		}

		/**
		 * @return Handler
		 */
		public function getHandler() {
			return Handler::getInstance();
		}

		public function handleError($code = null, $message = null, $file = null, $line = null) {
			$this->getHandler()->handleError($code, $message, $file, $line, null, 1);
		}

		public function handleException(\Exception $exception) {
			$this->getHandler()->handleException($exception);
		}

		public static function debug($message, $tags = 'debug') {
			Handler::getInstance()->debug($message, str_replace(',', '.', $tags), 1);
		}
	}
}

namespace {

	if(!class_exists('PhpConsole', false)) {
		class PhpConsole extends \PhpConsole\OldVersionAdapter {

		}
	}

	if(!function_exists('debug')) {
		function debug($message, $tags = 'debug') {
			\PhpConsole\Handler::getInstance()->debug($message, $tags, 1);
		}
	}
}
<?php

namespace PhpConsole;

/**
 * Overrides PHP errors and exceptions handlers, so all errors, exceptions and debug messages will be sent to PHP Console client
 * By default all handled errors and exceptions will be passed to previously defined handlers
 *
 * You will need to install Google Chrome extension "PHP Console"
 * https://chrome.google.com/webstore/detail/php-console/nfhmhhlpfleoednkpnnnkolmclajemef
 *
 * @package PhpConsole
 * @version 3.1
 * @link http://php-console.com
 * @author Sergey Barbushin http://linkedin.com/in/barbushin
 * @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
 * @license http://www.opensource.org/licenses/BSD-3-Clause "The BSD 3-Clause License"
 */
class Handler {

	const ERRORS_RECURSION_LIMIT = 3;

	/** @var Handler */
	protected static $instance;

	/** @var Connector */
	protected $connector;
	protected $isStarted = false;
	protected $isHandling = false;
	protected $errorsHandlerLevel;
	protected $handleErrors = true;
	protected $handleExceptions = true;
	protected $callOldHandlers = true;
	protected $oldErrorsHandler;
	protected $oldExceptionsHandler;
	protected $recursiveHandlingLevel = 0;

	/**
	 * @return static
	 */
	public static function getInstance() {
		if(!self::$instance) {
			self::$instance = new static();
		}
		return self::$instance;
	}

	protected function __construct() {
		$this->connector = Connector::getInstance();
	}

	private final function __clone() {
	}

	/**
	 * Start errors & exceptions handlers
	 * @throws \Exception
	 */
	public function start() {
		if($this->isStarted) {
			throw new \Exception(get_called_class() . ' is already started, use ' . get_called_class() . '::getInstance()->isStarted() to check it.');
		}
		$this->isStarted = true;

		if($this->handleErrors) {
			$this->initErrorsHandler();
		}
		if($this->handleExceptions) {
			$this->initExceptionsHandler();
		}
	}

	/**
	 * Validate that method is called before start. Required for handlers configuration methods.
	 * @throws \Exception
	 */
	protected function checkIsCalledBeforeStart() {
		if($this->isStarted) {
			throw new \Exception('This method can be called only before ' . get_class($this) . '::start()');
		}
	}

	/**
	 * Enable or disable errors handler
	 * @param bool $isEnabled
	 */
	public function setHandleErrors($isEnabled) {
		$this->checkIsCalledBeforeStart();
		$this->handleErrors = $isEnabled;
	}

	/**
	 * Enable or disable exceptions handler
	 * @param bool $isEnabled
	 */
	public function setHandleExceptions($isEnabled) {
		$this->checkIsCalledBeforeStart();
		$this->handleExceptions = $isEnabled;
	}

	/**
	 * Enable or disable calling overridden  errors & exceptions
	 * @param bool $isEnabled
	 */
	public function setCallOldHandlers($isEnabled) {
		$this->callOldHandlers = $isEnabled;
	}

	/**
	 * @return Connector
	 */
	public function getConnector() {
		return $this->connector;
	}

	/**
	 * Check if PHP Console handler is started
	 * @return bool
	 */
	public function isStarted() {
		return $this->isStarted;
	}

	/**
	 * Override PHP exceptions handler to PHP Console handler
	 */
	protected function initExceptionsHandler() {
		$this->oldExceptionsHandler = set_exception_handler(array($this, 'handleException'));
	}

	/**
	 * Set custom errors handler level like E_ALL ^ E_STRICT
	 * But, anyway, it's strongly recommended to configure ignore some errors type in PHP Console extension options
	 * IMPORTANT: previously old error handler will be called only with new errors level
	 * @param int $level see http://us1.php.net/manual/ru/function.error-reporting.php
	 */
	public function setErrorsHandlerLevel($level) {
		$this->checkIsCalledBeforeStart();
		$this->errorsHandlerLevel = $level;
	}

	/**
	 * Override PHP errors handler to PHP Console handler
	 */
	protected function initErrorsHandler() {
		ini_set('display_errors', false);
		ini_set('html_errors', false);
		error_reporting($this->errorsHandlerLevel ? : E_ALL | E_STRICT);
		$this->oldErrorsHandler = set_error_handler(array($this, 'handleError'));
		register_shutdown_function(array($this, 'checkFatalErrorOnShutDown'));
		$this->connector->registerFlushOnShutDown();
	}

	/**
	 * Method is called by register_shutdown_function(), it's required to handle fatal PHP errors. Never call it manually.
	 */
	public function checkFatalErrorOnShutDown() {
		$error = error_get_last();
		if($error) {
			ini_set('memory_limit', memory_get_usage(true) + 1000000); // if memory limit exceeded
			$this->callOldHandlers = false;
			$this->handleError($error['type'], $error['message'], $error['file'], $error['line'], null, 1);
		}
	}

	/**
	 * Handle error data
	 * @param int|null $code
	 * @param string|null $text
	 * @param string|null $file
	 * @param int|null $line
	 * @param null $context
	 * @param int $skipCallsLevel Number of proxy methods between original "error handler method" and this method call
	 */
	public function handleError($code = null, $text = null, $file = null, $line = null, $context = null, $skipCallsLevel = 0) {
		if(!$this->isStarted || error_reporting() === 0 || $this->isHandlingDisabled()) {
			return;
		}
		$this->onHandlingStart();
		$this->connector->getErrorsDispatcher()->dispatchError($code, $text, $file, $line, $skipCallsLevel + 1);
		if($this->oldErrorsHandler && $this->callOldHandlers) {
			call_user_func_array($this->oldErrorsHandler, array($code, $text, $file, $line, $context));
		}
		$this->onHandlingComplete();
	}

	/**
	 * Method is called before handling any error or exception
	 */
	protected function onHandlingStart() {
		$this->recursiveHandlingLevel++;
	}

	/**
	 * Method is called after handling any error or exception
	 */
	protected function onHandlingComplete() {
		$this->recursiveHandlingLevel--;
	}

	/**
	 * Check if errors/exception handling is disabled
	 * @return bool
	 */
	protected function isHandlingDisabled() {
		return $this->recursiveHandlingLevel >= static::ERRORS_RECURSION_LIMIT;
	}

	/**
	 * Handle exception object
	 * @param \Exception $exception
	 */
	public function handleException(\Exception $exception) {
		if(!$this->isStarted || $this->isHandlingDisabled()) {
			return;
		}
		try {
			$this->onHandlingStart();
			$this->connector->getErrorsDispatcher()->dispatchException($exception);
			if($this->oldExceptionsHandler && $this->callOldHandlers) {
				call_user_func($this->oldExceptionsHandler, $exception);
			}
		}
		catch(\Exception $internalException) {
			$this->handleException($internalException);
		}
		$this->onHandlingComplete();
	}

	/**
	 * Handle debug data
	 * @param mixed $data
	 * @param string|null $tags Tags separated by dot, e.g. "low.db.billing"
	 * @param int $skipTraceCalls Number of proxy methods between original "debug method call" and this method call
	 */
	public function debug($data, $tags = null, $skipTraceCalls = 0) {
		if($this->connector->isActiveClient()) {
			$this->connector->getDebugDispatcher()->dispatchDebug($data, $tags, $skipTraceCalls + 1);
		}
	}
}
<?php

namespace PhpConsole;

/**
 * Abstract class of dispatchers that sends different kind data to connector as client expected messages
 *
 * @package PhpConsole
 * @version 3.1
 * @link http://php-console.com
 * @author Sergey Barbushin http://linkedin.com/in/barbushin
 * @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
 * @license http://www.opensource.org/licenses/BSD-3-Clause "The BSD 3-Clause License"
 */
abstract class Dispatcher {

	/** @var  Connector */
	protected $connector;
	/** @var Dumper */
	protected $dumper;

	/**
	 * @param Connector $connector
	 * @param Dumper $dumper
	 */
	public function __construct(Connector $connector, Dumper $dumper) {
		$this->connector = $connector;
		$this->setDumper($dumper);
	}

	/**
	 * Override default dumper
	 * @param Dumper $dumper
	 */
	public function setDumper(Dumper $dumper) {
		$this->dumper = $dumper;
	}

	/**
	 * Check if dispatcher is active to send messages
	 * @return bool
	 */
	public function isActive() {
		return $this->connector->isActiveClient();
	}

	/**
	 * Send message to PHP Console connector
	 * @param Message $message
	 */
	protected function sendMessage(Message $message) {
		$this->connector->sendMessage($message);
	}

	/**
	 * Convert backtrace to array of TraceCall with source file & line detection
	 * @param array $trace Standard PHP backtrace array
	 * @param null|string $file Reference to var that will contain source file path
	 * @param null|string $line Reference to var that will contain source line number
	 * @param int $skipTraceCalls Last trace calls that will be stripped in result
	 * @return TraceCall[]
	 */
	protected function fetchTrace(array $trace, &$file = null, &$line = null, $skipTraceCalls = 0) {
		foreach($trace as $i => $call) {
			if(!$file && $i == $skipTraceCalls && isset($call['file'])) {
				$file = $call['file'];
				$line = $call['line'];
			}
			if($i < $skipTraceCalls || (isset($call['file']) && $call['file'] == $file && $call['line'] == $line)) {
				unset($trace[$i]);
			}
		}

		$traceCalls = array();
		foreach(array_reverse($trace) as $call) {
			$args = array();
			if(isset($call['args'])) {
				foreach($call['args'] as $arg) {
					if(is_object($arg)) {
						$args[] = get_class($arg);
					}
					elseif(is_array($arg)) {
						$args[] = 'Array[' . count($arg) . ']';
					}
					else {
						$arg = var_export($arg, 1);
						$args[] = strlen($arg) > 15 ? substr($arg, 0, 15) . '...\'' : $arg;
					}
				}
			}

			$traceCall = new TraceCall();
			$traceCall->call = (isset($call['class']) ? $call['class'] . $call['type'] : '') . $call['function'] . '(' . implode(', ', $args) . ')';
			if(isset($call['file'])) {
				$traceCall->file = $call['file'];
			}
			if(isset($call['line'])) {
				$traceCall->line = $call['line'];
			}
			$traceCalls[] = $traceCall;
		}
		return $traceCalls;
	}
}
<?php

namespace PhpConsole;

/**
 * Storage for postponed response data
 *
 * @package PhpConsole
 * @version 3.1
 * @link http://php-console.com
 * @author Sergey Barbushin http://linkedin.com/in/barbushin
 * @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
 * @license http://www.opensource.org/licenses/BSD-3-Clause "The BSD 3-Clause License"
 */
abstract class Storage {

	protected $keyLifetime = 60;

	/**
	 * Get postponed data from storage and delete
	 * @param string $key
	 * @return string
	 */
	abstract public function pop($key);

	/**
	 * Save postponed data to storage
	 * @param string $key
	 * @param string $data
	 */
	abstract public function push($key, $data);

	/**
	 * Set maximum key lifetime in seconds
	 * @param int $seconds
	 */
	public function setKeyLifetime($seconds) {
		$this->keyLifetime = $seconds;
	}
}
<?php

namespace PhpConsole\Dispatcher;

/**
 * Executes client code and sends result data to connector as client expected messages
 *
 * @package PhpConsole
 * @version 3.1
 * @link http://php-console.com
 * @author Sergey Barbushin http://linkedin.com/in/barbushin
 * @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
 * @license http://www.opensource.org/licenses/BSD-3-Clause "The BSD 3-Clause License"
 */
class Evaluate extends \PhpConsole\Dispatcher {

	/** @var \PhpConsole\EvalProvider */
	protected $evalProvider;

	/**
	 * @param \PhpConsole\Connector $connector
	 * @param \PhpConsole\EvalProvider $evalProvider
	 * @param \PhpConsole\Dumper $dumper
	 */
	public function __construct(\PhpConsole\Connector $connector, \PhpConsole\EvalProvider $evalProvider, \PhpConsole\Dumper $dumper) {
		$this->evalProvider = $evalProvider;
		parent::__construct($connector, $dumper);
	}

	/**
	 * Override eval provider
	 * @param \PhpConsole\EvalProvider $evalProvider
	 */
	public function setEvalProvider(\PhpConsole\EvalProvider $evalProvider) {
		$this->evalProvider = $evalProvider;
	}

	/**
	 * Get eval provider
	 * @return \PhpConsole\EvalProvider
	 */
	public function getEvalProvider() {
		return $this->evalProvider;
	}

	/**
	 * Execute PHP code and send result message in connector
	 * @param $code
	 */
	public function dispatchCode($code) {
		if($this->isActive()) {
			$previousLastError = error_get_last();
			$oldDisplayErrors = ini_set('display_errors', false);
			$result = $this->evalProvider->evaluate($code);
			ini_set('display_errors', $oldDisplayErrors);

			$message = new \PhpConsole\EvalResultMessage();
			$message->return = $this->dumper->dump($result->return);
			$message->output = $this->dumper->dump($result->output);
			$message->time = round($result->time, 6);

			$newLastError = error_get_last();
			if($newLastError && $newLastError != $previousLastError) {
				$this->connector->getErrorsDispatcher()->dispatchError($newLastError ['type'], $newLastError ['message'], $newLastError ['file'], $newLastError ['line'], 999);
			}
			if($result->exception) {
				$this->connector->getErrorsDispatcher()->dispatchException($result->exception);
			}
			$this->sendMessage($message);
		}
	}
}
<?php

namespace PhpConsole\Dispatcher;

/**
 * Sends debug data to connector as client expected messages
 *
 * @package PhpConsole
 * @version 3.1
 * @link http://php-console.com
 * @author Sergey Barbushin http://linkedin.com/in/barbushin
 * @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
 * @license http://www.opensource.org/licenses/BSD-3-Clause "The BSD 3-Clause License"
 */
class Debug extends \PhpConsole\Dispatcher {

	/** @var bool Autodetect and append trace data to debug */
	public $detectTraceAndSource = false;

	/**
	 * Send debug data message to client
	 * @param mixed $data
	 * @param null|string $tags Tags separated by dot, e.g. "low.db.billing"
	 * @param null|int $callLevel Number of proxy methods between original "debug call" and this method call
	 */
	public function dispatchDebug($data, $tags = null, $callLevel = 0) {
		if($this->isActive()) {
			$message = new \PhpConsole\DebugMessage();
			$message->data = $this->dumper->dump($data);
			if($tags) {
				$message->tags = explode('.', $tags);
			}
			if($this->detectTraceAndSource && $callLevel !== null) {
				$message->trace = $this->fetchTrace(debug_backtrace(), $message->file, $message->line, $callLevel);
			}
			$this->sendMessage($message);
		}
	}
}
<?php

namespace PhpConsole\Dispatcher;

/**
 * Sends system errors and exceptions to connector as client expected messages
 *
 * @package PhpConsole
 * @version 3.1
 * @link http://php-console.com
 * @author Sergey Barbushin http://linkedin.com/in/barbushin
 * @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
 * @license http://www.opensource.org/licenses/BSD-3-Clause "The BSD 3-Clause License"
 */
class Errors extends \PhpConsole\Dispatcher {

	/** @var array PHP errors constants values => names(will be initialized in first call) */
	protected static $errorsConstantsValues = array();
	/** @var array PHP errors constants names */
	protected static $errorsConstantsNames = array(
		'E_STRICT',
		'E_DEPRECATED',
		'E_RECOVERABLE_ERROR',
		'E_NOTICE',
		'E_WARNING',
		'E_ERROR',
		'E_PARSE',
		'E_USER_DEPRECATED',
		'E_USER_NOTICE',
		'E_USER_WARNING',
		'E_USER_ERROR',
		'E_CORE_WARNING',
		'E_CORE_ERROR',
		'E_COMPILE_ERROR',
		'E_COMPILE_WARNING',
	);

	/** @var bool Don't send errors messages with same file, line & class */
	public $ignoreRepeatedSource = true;
	/** @var bool Dispatch $exception->getPrevious() if not empty */
	public $dispatchPreviousExceptions = true;

	/** @var \PhpConsole\ErrorMessage[] */
	protected $sentMessages = array();

	/**
	 * Send error message to client
	 * @param null|integer $code
	 * @param null|string $text
	 * @param null|string $file
	 * @param null|integer $line
	 * @param null|int $callLevel Number of proxy methods between original "error handler method" and this method call
	 */
	public function dispatchError($code = null, $text = null, $file = null, $line = null, $callLevel = 0) {
		if($this->isActive()) {
			$message = new \PhpConsole\ErrorMessage();
			$message->code = $code;
			$message->class = $this->getErrorTypeByCode($code);
			$message->data = $this->dumper->dump($text);
			$message->file = $file;
			$message->line = $line;
			if($callLevel !== null) {
				$message->trace = $this->fetchTrace(debug_backtrace(), $file, $line, $callLevel + 1);
			}
			$this->sendMessage($message);
		}
	}

	/**
	 * Send exception message to client
	 * @param \Exception $exception
	 */
	public function dispatchException(\Exception $exception) {
		if($this->isActive()) {
			if($this->dispatchPreviousExceptions && $exception->getPrevious()) {
				$this->dispatchException($exception->getPrevious());
			}
			$message = new \PhpConsole\ErrorMessage();
			$message->code = $exception->getCode();
			$message->class = get_class($exception);
			$message->data = $this->dumper->dump($exception->getMessage());
			$message->file = $exception->getFile();
			$message->line = $exception->getLine();
			$message->trace = self::fetchTrace($exception->getTrace(), $message->file, $message->line);
			$this->sendMessage($message);
		}
	}

	/**
	 * Send message to PHP Console connector
	 * @param \PhpConsole\Message $message
	 */
	protected function sendMessage(\PhpConsole\Message $message) {
		if(!$this->isIgnored($message)) {
			parent::sendMessage($message);
			$this->sentMessages[] = $message;
		}
	}

	/**
	 * Get PHP error constant name by value
	 * @param int $code
	 * @return string
	 */
	protected function getErrorTypeByCode($code) {
		if(!static::$errorsConstantsValues) {
			foreach(static::$errorsConstantsNames as $constantName) {
				if(defined($constantName)) {
					static::$errorsConstantsValues[constant($constantName)] = $constantName;
				}
			}
		}
		if(isset(static::$errorsConstantsValues[$code])) {
			return static::$errorsConstantsValues[$code];
		}
		return (string)$code;
	}

	/**
	 * Return true if message with same file, line & class was already sent
	 * @param \PhpConsole\ErrorMessage $message
	 * @return bool
	 */
	protected function isIgnored(\PhpConsole\ErrorMessage $message) {
		if($this->ignoreRepeatedSource && $message->file) {
			foreach($this->sentMessages as $sentMessage) {
				if($message->file == $sentMessage->file && $message->line == $sentMessage->line && $message->class == $sentMessage->class) {
					return true;
				}
			}
		}
		return false;
	}
}
<?php

namespace PhpConsole {

	/**
	 * Makes more easy access to debug dispatcher method.
	 *
	 * Usage:
	 * 1. Call \PhpConsole\Helper::register();
	 * 2. Call PC::debug($sql, 'db') or PC::db($sql)
	 *
	 * It will be the same as calling Handler::getInstance()->debug($var, 'db')
	 *
	 * @package PhpConsole
	 * @version 3.1
	 * @link http://php-console.com
	 * @author Sergey Barbushin http://linkedin.com/in/barbushin
	 * @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
	 * @license http://www.opensource.org/licenses/BSD-3-Clause "The BSD 3-Clause License"
	 */
	class Helper {

		/** @var Connector|null */
		private static $connector;
		/** @var Handler|null */
		private static $handler;
		/** @var  bool */
		protected static $isActive;

		private function __construct() {
		}

		/**
		 * This method must be called to make class "PC" available
		 * @param Connector|null $connector
		 * @param Handler|null $handler
		 * @throws \Exception
		 * @return Connector
		 */
		public static function register(Connector $connector = null, Handler $handler = null) {
			if(static::$connector) {
				throw new \Exception('Helper already registered');
			}
			self::$handler = $handler;
			self::$connector = $connector ? : Connector::getInstance();
			self::$isActive = self::$connector->isActiveClient();
			return self::$connector;
		}

		/**
		 * Check if method Helper::register() was called before
		 * @return bool
		 */
		public static function isRegistered() {
			return isset(self::$connector);
		}

		/**
		 * Get actual helper connector instance
		 * @return Connector
		 * @throws \Exception
		 */
		public static function getConnector() {
			if(!self::$connector) {
				throw new \Exception('Helper is not registered. Call ' . get_called_class() . '::register()');
			}
			return self::$connector;
		}

		/**
		 * Get actual handler instance
		 * @return Handler
		 * @throws \Exception
		 */
		public static function getHandler() {
			if(!self::$connector) {
				throw new \Exception('Helper is not registered. Call ' . get_called_class() . '::register()');
			}
			if(!self::$handler) {
				self::$handler = Handler::getInstance();
			}
			return self::$handler;
		}

		/**
		 * Analog of Handler::getInstance()->debug(...) method
		 * @param mixed $data
		 * @param string|null $tags Tags separated by dot, e.g. "low.db.billing"
		 * @param int $skipTraceCalls Number of proxy methods between original "debug method call" and this method call
		 */
		public static function debug($data, $tags = null, $skipTraceCalls = 0) {
			if(self::$isActive) {
				self::$connector->getDebugDispatcher()->dispatchDebug($data, $tags, $skipTraceCalls + 1);
			}
		}

		/**
		 * Short access to analog of Handler::getInstance()->debug(...) method
		 * You can access it like PC::tagName($debugData, $additionalTags = null)
		 * @param string $tags
		 * @param $args
		 */
		public static function __callStatic($tags, $args) {
			if(isset($args[1])) {
				$tags .= '.' . $args[1];
			}
			static::debug(isset($args[0]) ? $args[0] : null, $tags, 1);
		}
	}
}

namespace {
	if(!class_exists('PC', false)) {
		/**
		 * Helper short class name in global namespace
		 */
		class PC extends \PhpConsole\Helper {

		}
	}
}
<?php

namespace PhpConsole;

/**
 * Convert any type of var to string or array with different kind of limits
 *
 * @package PhpConsole
 * @version 3.1
 * @link http://php-console.com
 * @author Sergey Barbushin http://linkedin.com/in/barbushin
 * @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
 * @license http://www.opensource.org/licenses/BSD-3-Clause "The BSD 3-Clause License"
 */
class Dumper {

	/** @var int Maximum array or object nested dump level */
	public $levelLimit;
	/** @var  int Maximum same level array items or object properties number */
	public $itemsCountLimit;
	/** @var  int Maximum length of any string or array item */
	public $itemSizeLimit;
	/** @var int|null Maximum approximate size of dump result formatted in JSON */
	public $dumpSizeLimit;
	/** @var bool Convert callback items to (callback SomeClass::someMethod) strings */
	public $detectCallbacks = true;

	/**
	 * @param int $levelLimit Maximum array or object nested dump level
	 * @param int $itemsCountLimit Maximum same level array items or object properties number
	 * @param int $itemSizeLimit Maximum length of any string or array item
	 * @param int $dumpSizeLimit Maximum approximate size of dump result formatted in JSON. Default is $itemsCountLimit * $itemSizeLimit
	 */
	public function __construct($levelLimit = 5, $itemsCountLimit = 100, $itemSizeLimit = 50000, $dumpSizeLimit = 500000) {
		$this->levelLimit = $levelLimit;
		$this->itemsCountLimit = $itemsCountLimit;
		$this->itemSizeLimit = $itemSizeLimit;
		$this->dumpSizeLimit = $dumpSizeLimit;
	}

	/**
	 * Convert any type of var to string or array applying all actual limits & transformations
	 * @param mixed $var
	 * @return string|array
	 */
	public function dump($var) {
		$this->dumpVarData($var, $this->levelLimit);
		return $var;
	}

	/**
	 * Recursively convert any type of var to string or array applying all actual limits & transformations
	 * @param $data
	 * @param $levelLimit
	 * @param bool $rootCall
	 */
	protected function dumpVarData(&$data, $levelLimit, $rootCall = true) {
		static $sizeLeft,
		$objectsHashes = array(),
		$origQueue = array(),
		$refQueue = array(),
		$levelsQueue = array();

		if($rootCall) {
			$sizeLeft = $this->dumpSizeLimit ? : 999999999;
		}

		if(is_object($data)) {
			if($data instanceof \Closure) {
				$data = '(callback function)';
				return;
			}
			if($rootCall) {
				$data = array('' => $data);
				return $this->dumpVarData($data, $levelLimit + 1);
			}
			$objectsHashes[] = spl_object_hash($data);
			$dataArray = array();
			foreach((array)$data as $key => $value) {
				$nullPos = strrpos($key, chr(0));
				if($nullPos) {
					$dataArray[substr($key, $nullPos + 1)] = $value;
				}
				else {
					$dataArray[$key] = $value;
				}
			}
			$data = $dataArray;
		}

		if(is_array($data)) {

			if($this->detectCallbacks && count($data) == 2 && is_callable($data)) {
				list($class, $method) = $data;
				$data = '(callback ' . (is_object($class) ? get_class($class) : $class) . '::' . $method . ')';
				$sizeLeft -= strlen($data) + 4;
				return;
			}

			$i = 0;
			$dataArray = array();
			foreach($data as $k => &$v) {
				if(($this->itemsCountLimit && $i >= $this->itemsCountLimit) || $sizeLeft <= 0) {
					break;
				}
				if(is_array($v) || is_object($v)) {
					if($levelLimit > 1) {
						$origQueue[] = $v;
						$refQueue[] =& $v;
						$levelsQueue[] = $levelLimit;
					}
					if(is_object($v) && !$v instanceof \Closure) {
						$k .= ':' . get_class($v);
						$hash = spl_object_hash($v);
						if(in_array($hash, $objectsHashes)) {
							$v = '*RECURSION*';
						}
						else {
							$v = '(object)';
							$objectsHashes[] = $hash;
						}
					}
					else {
						$v = '(array)';
					}
					$sizeLeft -= strlen($k) + strlen($v) + 8;
				}
				else {
					$sizeLeft -= strlen($k) + 4;
					$this->dumpVarData($v, $levelLimit - 1, false);
				}
				$dataArray[$k] =& $v;
				$i++;
			}

			if($i != count($data)) {
				$dataArray['...'] = '(displayed ' . $i . ' of ' . count($data) . ')';
			}
			$data = $dataArray;

			if(!$rootCall) {
				return;
			}

			do {
				$origData = array_shift($origQueue);
				$level = array_shift($levelsQueue);
				$refData =& $refQueue[0];
				array_shift($refQueue);
				$sizeLeft += strlen($refData);
				if($refData !== '*RECURSION*') {
					$this->dumpVarData($origData, $level - 1, false);
					$refData = $origData;
				}
			}
			while(count($origQueue) && $sizeLeft >= 0);

			if($rootCall) {
				$levelsQueue = $origQueue = $refQueue = $objectsHashes = array();
			}
		}
		// scalar or resource
		else {
			if(!is_scalar($data) && $data !== null) {
				if(is_resource($data)) {
					$data = '(' . strtolower((string)$data) . ' ' . get_resource_type($data) . ')';
					$sizeLeft -= strlen($data);
					return;
				}
				$data = var_export($data, true);
			}
			if(strlen($data) > $this->itemSizeLimit) {
				$data = substr($data, 0, $this->itemSizeLimit - 3) . '...';
			}
			if(strlen($data) > $sizeLeft) {
				$data = substr($data, 0, $sizeLeft - 3) . '...';
			}
			$sizeLeft -= strlen($data);
		}
	}
}
<?php

namespace PhpConsole\Storage;

/**
 * $_SESSION storage for postponed response data. Is used by default.
 *
 * @package PhpConsole
 * @version 3.1
 * @link http://php-console.com
 * @author Sergey Barbushin http://linkedin.com/in/barbushin
 * @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
 * @license http://www.opensource.org/licenses/BSD-3-Clause "The BSD 3-Clause License"
 */
class Session extends AllKeysList {

	protected $sessionKey;

	/**
	 * @param string $sessionKey Key name in $_SESSION variable
	 * @param bool $autoStart Start session if it's not started
	 */
	public function __construct($sessionKey = '__PHP_Console_postponed', $autoStart = true) {
		if($autoStart && (defined('PHP_SESSION_ACTIVE') ? session_status() != PHP_SESSION_ACTIVE : !session_id())) {
			session_start();
		}
		register_shutdown_function('session_write_close'); // force saving session data if session handler is overridden
		$this->sessionKey = $sessionKey;
	}

	protected function getKeysData() {
		return isset($_SESSION[$this->sessionKey]) ? $_SESSION[$this->sessionKey] : array();
	}

	protected function saveKeysData(array $keysData) {
		$_SESSION[$this->sessionKey] = $keysData;
	}
}
<?php

namespace PhpConsole\Storage;

/**
 * Memcache storage for postponed response data.
 *
 * @package PhpConsole
 * @version 3.1
 * @link http://php-console.com
 * @author Sergey Barbushin http://linkedin.com/in/barbushin
 * @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
 * @license http://www.opensource.org/licenses/BSD-3-Clause "The BSD 3-Clause License"
 */
class Memcache extends ExpiringKeyValue {

	/** @var  \Memcache */
	protected $memcache;

	public function __construct($host = 'localhost', $port = 11211) {
		$this->memcache = new \Memcache();
		if(!$this->memcache->connect($host, $port)) {
			throw new \Exception('Unable to connect to Memcache server');
		}
	}

	/**
	 * Save data by auto-expire key
	 * @param $key
	 * @param string $data
	 * @param int $expire
	 */
	protected function set($key, $data, $expire) {
		$this->memcache->set($key, $data, null, $expire);
	}

	/**
	 * Get data by key if not expired
	 * @param $key
	 * @return string
	 */
	protected function get($key) {
		return $this->memcache->get($key);
	}

	/**
	 * Remove key in store
	 * @param $key
	 * @return mixed
	 */
	protected function delete($key) {
		$this->memcache->delete($key);
	}
}
<?php

namespace PhpConsole\Storage;

/**
 * File storage for postponed response data. Use it if session handler in your project is overridden.
 *
 * @package PhpConsole
 * @version 3.1
 * @link http://php-console.com
 * @author Sergey Barbushin http://linkedin.com/in/barbushin
 * @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
 * @license http://www.opensource.org/licenses/BSD-3-Clause "The BSD 3-Clause License"
 */
class File extends AllKeysList {

	protected $filePath;
	protected $fileHandler;

	/**
	 * @param string $filePath Writable path for postponed data storage (should not be under DOCUMENT_ROOT)
	 * @param bool $validatePathNotUnderDocRoot Throw \Exception if $filePath is not under DOCUMENT_ROOT
	 * @throws \Exception
	 */
	public function __construct($filePath, $validatePathNotUnderDocRoot = true) {
		if(!file_exists($filePath)) {
			if(file_put_contents($filePath, '') === false) {
				throw new \Exception('Unable to write file ' . $filePath);
			}
		}
		$this->filePath = realpath($filePath);

		if($validatePathNotUnderDocRoot && $this->isPathUnderDocRoot()) {
			throw new \Exception('Path ' . $this->filePath . ' is under DOCUMENT_ROOT. It\'s insecure!');
		}
	}

	protected function isPathUnderDocRoot() {
		return !empty($_SERVER['DOCUMENT_ROOT']) && strpos($this->filePath, $_SERVER['DOCUMENT_ROOT']) === 0;
	}

	protected function initFileHandler() {
		$this->fileHandler = fopen($this->filePath, 'a+b');
		if(!$this->fileHandler) {
			throw new \Exception('Unable to read/write file ' . $this->filePath);
		}
		while(!flock($this->fileHandler, LOCK_EX | LOCK_NB)) {
			usleep(10000);
		}
		fseek($this->fileHandler, 0);
	}

	/**
	 * @throws \Exception
	 * @return array
	 */
	protected function getKeysData() {
		return json_decode(fgets($this->fileHandler), true) ? : array();
	}

	/**
	 * @param array $keysData
	 */
	protected function saveKeysData(array $keysData) {
		ftruncate($this->fileHandler, 0);
		fwrite($this->fileHandler, json_encode($keysData, defined('JSON_UNESCAPED_UNICODE') ? JSON_UNESCAPED_UNICODE : null));
	}

	protected function closeFileHandler() {
		if($this->fileHandler) {
			flock($this->fileHandler, LOCK_UN);
			fclose($this->fileHandler);
			$this->fileHandler = null;
		}
	}

	public function pop($key) {
		$this->initFileHandler();
		$result = parent::pop($key);
		$this->closeFileHandler();
		return $result;
	}

	public function push($key, $data) {
		$this->initFileHandler();
		parent::push($key, $data);
		$this->closeFileHandler();
	}

	public function __destruct() {
		$this->closeFileHandler();
	}
}
<?php

namespace PhpConsole\Storage;

/**
 * Abstract class for key-value stores with key auto-expire support
 *
 * @package PhpConsole
 * @version 3.1
 * @link http://php-console.com
 * @author Sergey Barbushin http://linkedin.com/in/barbushin
 * @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
 * @license http://www.opensource.org/licenses/BSD-3-Clause "The BSD 3-Clause License"
 */
abstract class ExpiringKeyValue extends \PhpConsole\Storage {

	/**
	 * Save data by auto-expire key
	 * @param $key
	 * @param string $data
	 * @param int $expire
	 */
	abstract protected function set($key, $data, $expire);

	/**
	 * Get data by key if not expired
	 * @param $key
	 * @return string
	 */
	abstract protected function get($key);

	/**
	 * Remove key in store
	 * @param $key
	 * @return mixed
	 */
	abstract protected function delete($key);

	/**
	 * Get postponed data from storage and delete
	 * @param string $key
	 * @return string
	 */
	public function pop($key) {
		$data = $this->get($key);
		if($data) {
			$this->delete($key);
		}
		return $data;
	}

	/**
	 * Save postponed data to storage
	 * @param string $key
	 * @param string $data
	 */
	public function push($key, $data) {
		$this->set($key, $data, $this->keyLifetime);
	}
}
<?php

namespace PhpConsole\Storage;

/**
 * Abstract class for stores that manipulates with all keys data in memory
 *
 * @package PhpConsole
 * @version 3.1
 * @link http://php-console.com
 * @author Sergey Barbushin http://linkedin.com/in/barbushin
 * @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
 * @license http://www.opensource.org/licenses/BSD-3-Clause "The BSD 3-Clause License"
 */
abstract class AllKeysList extends \PhpConsole\Storage {

	/**
	 * Get all postponed keys data
	 * @return array
	 */
	abstract protected function getKeysData();

	/**
	 * Save all postponed keys data
	 * @param array $keysData
	 */
	abstract protected function saveKeysData(array $keysData);

	/**
	 * Get postponed data from storage and delete
	 * @param string $key
	 * @return string
	 */
	public function pop($key) {
		$keysData = $this->getKeysData();
		if(isset($keysData[$key])) {
			$keyData = $keysData[$key]['data'];
			unset($keysData[$key]);
			$this->saveKeysData($keysData);
			return $keyData;
		}
	}

	/**
	 * Save postponed data to storage
	 * @param string $key
	 * @param string $data
	 */
	public function push($key, $data) {
		$keysData = $this->getKeysData();
		$this->clearExpiredKeys($keysData);
		$keysData[$key] = array(
			'time' => time(),
			'data' => $data
		);
		$this->saveKeysData($keysData);
	}

	/**
	 * Remove postponed data that is out of limit
	 * @param array $keysData
	 */
	protected function clearExpiredKeys(array &$keysData) {
		$expireTime = time() - $this->keyLifetime;
		foreach($keysData as $key => $item) {
			if($item['time'] < $expireTime) {
				unset($keysData[$key]);
			}
		}
	}
}
<?php

namespace PhpConsole;

use Psr\Log\LogLevel;

/**
 * Implementation of PSR-3 logger interface https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
 * IMPORTANT: https://github.com/php-fig/log must be installed & autoloaded
 *
 * @package PhpConsole
 * @version 3.1
 * @link http://php-console.com
 * @author Sergey Barbushin http://linkedin.com/in/barbushin
 * @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
 * @license http://www.opensource.org/licenses/BSD-3-Clause "The BSD 3-Clause License"
 */
class PsrLogger extends \Psr\Log\AbstractLogger {

	public static $debugLevels = array(
		LogLevel::NOTICE => 'notice',
		LogLevel::INFO => 'info',
		LogLevel::DEBUG => 'debug',
	);

	public static $errorsLevels = array(
		LogLevel::EMERGENCY => 'PSR_EMERGENCY',
		LogLevel::ALERT => 'PSR_ALERT',
		LogLevel::CRITICAL => 'PSR_CRITICAL',
		LogLevel::ERROR => 'PSR_ERROR',
		LogLevel::WARNING => 'PSR_WARNING',
	);

	/** @var  Connector */
	protected $connector;
	/** @var  Dumper */
	protected $contextDumper;

	public function __construct(Connector $connector = null, Dumper $contextDumper = null) {
		$this->connector = $connector ? : Connector::getInstance();
		$this->contextDumper = $contextDumper ? : $this->connector->getDumper();
	}

	/**
	 * Logs with an arbitrary level.
	 *
	 */
	public function log($level, $message, array $context = array()) {
		if(is_object($message) && is_callable($message, '__toString')) {
			$message = (string)$message;
		}
		$message = $this->fetchMessageContext($message, $context);

		if(isset(static::$debugLevels[$level])) {
			$this->connector->getDebugDispatcher()->dispatchDebug($message, static::$debugLevels[$level], null);
		}
		elseif(isset(static::$errorsLevels[$level])) {
			if(isset($context['exception']) && $context['exception'] instanceof \Exception) {
				$this->connector->getErrorsDispatcher()->dispatchException($context['exception']);
			}
			else {
				$this->connector->getErrorsDispatcher()->dispatchError(static::$errorsLevels[$level], $message, null, null, null);
			}
		}
		else {
			throw new \Psr\Log\InvalidArgumentException('Unknown log level "' . $level . '"');
		}
	}

	protected function fetchMessageContext($message, array $context) {
		$replace = array();
		foreach($context as $key => $value) {
			$replace['{' . $key . '}'] = $this->contextDumper->dump($value);
		}
		return strtr($message, $replace);
	}
}
<?php

namespace PhpConsole;

/**
 * PHP Console client authorization credentials & validation class
 *
 * @package PhpConsole
 * @version 3.1
 * @link http://php-console.com
 * @author Sergey Barbushin http://linkedin.com/in/barbushin
 * @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
 * @license http://www.opensource.org/licenses/BSD-3-Clause "The BSD 3-Clause License"
 */
class Auth {

	const PASSWORD_HASH_SALT = 'NeverChangeIt:)';

	protected $publicKeyByIp;
	protected $passwordHash;

	/**
	 * @param string $password Common password for all clients
	 * @param bool $publicKeyByIp Set public key depending on client IP
	 */
	public function __construct($password, $publicKeyByIp = true) {
		$this->publicKeyByIp = $publicKeyByIp;
		$this->passwordHash = $this->getPasswordHash($password);
	}

	protected final function hash($string) {
		return hash('sha256', $string);
	}

	/**
	 * Get password hash like on client
	 * @param $password
	 * @return string
	 */
	protected final function getPasswordHash($password) {
		return $this->hash($password . self::PASSWORD_HASH_SALT);
	}

	/**
	 * Get authorization result data for client
	 * @param ClientAuth|null $clientAuth
	 * @return ServerAuthStatus
	 */
	public final function getServerAuthStatus(ClientAuth $clientAuth = null) {
		$serverAuthStatus = new ServerAuthStatus();
		$serverAuthStatus->publicKey = $this->getPublicKey();
		$serverAuthStatus->isSuccess = $clientAuth && $this->isValidAuth($clientAuth);
		return $serverAuthStatus;
	}

	/**
	 * Check if client authorization data is valid
	 * @param ClientAuth $clientAuth
	 * @return bool
	 */
	public final function isValidAuth(ClientAuth $clientAuth) {
		return $clientAuth->publicKey === $this->getPublicKey() && $clientAuth->token === $this->getToken();
	}

	/**
	 * Get client unique identification
	 * @return string
	 */
	protected function getClientUid() {
		$clientUid = '';
		if($this->publicKeyByIp) {
			if(isset($_SERVER['REMOTE_ADDR'])) {
				$clientUid .= $_SERVER['REMOTE_ADDR'];
			}
			if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
				$clientUid .= $_SERVER['HTTP_X_FORWARDED_FOR'];
			}
		}
		return $clientUid;
	}

	/**
	 * Get authorization session public key for current client
	 * @return string
	 */
	protected function getPublicKey() {
		return $this->hash($this->getClientUid() . $this->passwordHash);
	}

	/**
	 * Get string signature for current password & public key
	 * @param $string
	 * @return string
	 */
	public final function getSignature($string) {
		return $this->hash($this->passwordHash . $this->getPublicKey() . $string);
	}

	/**
	 * Get expected valid client authorization token
	 * @return string
	 */
	private final function getToken() {
		return $this->hash($this->passwordHash . $this->getPublicKey());
	}
}
_64s,s	{k   GBMB