Skip to content

Commit

Permalink
v3.1 Release with more stable client-server protocol
Browse files Browse the repository at this point in the history
Fixed: headers problem if flush() or ob_end_clean() is called before script shutdown
Fixed: problem with frameworks that overrides $_SESSION handler see https://github.com/barbushin/php-console#troubleshooting-with-_session-handler-overridden-in-some-frameworks
Migration: Update PHP Console extension to last version >= 3.0.20

[Fixes #34]
[Relative #36]
  • Loading branch information
barbushin committed Jan 15, 2014
1 parent 07801cb commit 22f87b9
Show file tree
Hide file tree
Showing 26 changed files with 574 additions and 70 deletions.
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,24 @@ You can try most of PHP Console features on [live demo](http://php-console.com/i

## Connector

There is a [PhpConsole\Connector](src/PhpConsole/Connector.php) class that initializes connection between PHP server and Google Chrome extension. Connection is initalized when `PhpConsole\Connector` instance is initialized:
There is a [PhpConsole\Connector](src/PhpConsole/Connector.php) class that initializes connection between PHP server and Google Chrome extension. Connection is initalized when [PhpConsole\Connector](src/PhpConsole/Connector.php) instance is initialized:

$connector = PhpConsole\Connector::getInstance();

`PhpConsole\Connector` uses headers to communicate with client, so it must be initialized before any output. Also there must be no `flush()` and `while(ob_get_level()) ob_end_clean();` calls and etc code flushing headers.
Also it will be initialized when you call `PhpConsole\Handler::getInstance()` or `PhpConsole\Helper::register()`.

### Communication protocol

PHP Console uses headers to communicate with client, so `PhpConsole\Connector::getInstance()` or `PhpConsole\Handler::getInstance()` must be called before any output. If headers are sent before script shut down or PHP Console response package size is out of web-server headers size limit, then PHP Console will store response data in [PhpConsole\Storage](src/PhpConsole/Storage.php) implementation and send it to client in STDOUT, in additional HTTP request. So there is no limits in PHP Console response package size.

### Troubleshooting with $_SESSION handler overridden in some frameworks

By default PHP Console uses [PhpConsole\Storage\Session](src/PhpConsole/Storage/Session.php) for postponed responses, so all temporary data will be stored in `$_SESSION`. But there is some problem with frameworks like [Symfony](http://symfony.com) and [Laravel](http://laravel.com) that overrides PHP session handler. In this case you should use any other [PhpConsole\Storage](src/PhpConsole/Storage.php) implementation like:

// Can be called only before PhpConsole\Connector::getInstance() and PhpConsole\Handler::getInstance()
PhpConsole\Connector::setPostponeStorage(new PhpConsole\Storage\File('/tmp/pc.data'));

See all available [PhpConsole\Storage](src/PhpConsole/Storage.php) implementations in [/src/PhpConsole/Storage](src/PhpConsole/Storage).

### Strip sources base path

Expand Down
2 changes: 1 addition & 1 deletion src/PhpConsole/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* PHP Console client authorization credentials & validation class
*
* @package PhpConsole
* @version 3.0
* @version 3.1
* @link http://php-console.com
* @author Sergey Barbushin http://linkedin.com/in/barbushin
* @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
Expand Down
108 changes: 52 additions & 56 deletions src/PhpConsole/Connector.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* https://chrome.google.com/webstore/detail/php-console/nfhmhhlpfleoednkpnnnkolmclajemef
*
* @package PhpConsole
* @version 3.0
* @version 3.1
* @link http://php-console.com
* @author Sergey Barbushin http://linkedin.com/in/barbushin
* @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
Expand All @@ -22,14 +22,16 @@ class Connector {
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 SESSION_KEY = '__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;
Expand All @@ -40,7 +42,7 @@ class Connector {
/** @var Dispatcher\Evaluate|null */
protected $evalDispatcher;
/** @var string */
protected $serverEncoding;
protected $serverEncoding = self::CLIENT_ENCODING;
protected $sourcesBasePath;
protected $headersLimit;

Expand All @@ -50,6 +52,7 @@ class Connector {
private $auth;
/** @var Message[] */
private $messages = array();
private $postponeResponseId;
private $isSslOnlyMode = false;
private $isActiveClient = false;
private $isAuthorized = false;
Expand All @@ -66,6 +69,29 @@ public static function getInstance() {
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()');
}
static::$postponeStorage = $storage;
}

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

protected function __construct() {
$this->initConnection();
$this->setServerEncoding(ini_get('mbstring.internal_encoding') ? : self::CLIENT_ENCODING);
Expand Down Expand Up @@ -102,8 +128,8 @@ private final function initConnection() {
? 4096 // default headers limit for Nginx
: 8192 // default headers limit for all other web-servers
);

$this->listenGetPostponedResponse();
$this->postponeResponseId = $this->setPostponeHeader();
}
}

Expand Down Expand Up @@ -463,17 +489,31 @@ private final function proceedResponsePackage() {

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

if(strlen($responseData) > $this->headersLimit) {
$responseData = $this->serializeResponse(new PostponedResponse(array(
'id' => $this->postponeResponse($responseData)
)));
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;
}

if(headers_sent($file, $line)) {
throw new \Exception('Unable to process response data, headers already sent in ' . $file . ':' . $line . '. Try to use ob_start()');
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().');
}
header(self::HEADER_NAME . ': ' . $responseData);
return false;
}
header($headerName . ': ' . $responseData);
return true;
}

protected function objectToArray(&$var) {
Expand All @@ -497,55 +537,11 @@ protected function serializeResponse(DataObject $response) {
private final function listenGetPostponedResponse() {
if(isset($_POST[self::POST_VAR_NAME]['getPostponedResponse'])) {
header('Content-Type: application/json; charset=' . self::CLIENT_ENCODING);
echo $this->getPostponedResponse($_POST[self::POST_VAR_NAME]['getPostponedResponse']);
echo $this->getPostponeStorage()->pop($_POST[self::POST_VAR_NAME]['getPostponedResponse']);
$this->breakClientConnection();
exit;
}
}

/**
* Store postponed response data
* @param string $responseData
* @return string id
*/
protected function postponeResponse($responseData) {
$responses =& $this->getSessionPostponedResponses();
while(count($responses) >= static::POSTPONE_REQUESTS_LIMIT) {
array_shift($responses);
}
$id = mt_rand() . mt_rand();
$responses[$id] = $responseData;
return $id;
}

/**
* Get postponed response data by id
* @param string $responseId
* @return string|null
*/
protected function getPostponedResponse($responseId) {
$responses =& $this->getSessionPostponedResponses();
if(isset($responses[$responseId])) {
$responseData = $responses[$responseId];
unset($responses[$responseId]);
return $responseData;
}
}

/**
* Returns reference to postponed responses array in $_SESSION
* @return array
*/
protected function &getSessionPostponedResponses() {
if(PHP_VERSION >= '5.4' ? session_status() != PHP_SESSION_ACTIVE : !session_id()) {
session_start();
register_shutdown_function('session_write_close'); // force saving session data if session handler is overridden
}
if(!isset($_SESSION[static::SESSION_KEY]['postpone'])) {
$_SESSION[static::SESSION_KEY]['postpone'] = array();
}
return $_SESSION[static::SESSION_KEY]['postpone'];
}
}

abstract class DataObject {
Expand Down
2 changes: 1 addition & 1 deletion src/PhpConsole/Dispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Abstract class of dispatchers that sends different kind data to connector as client expected messages
*
* @package PhpConsole
* @version 3.0
* @version 3.1
* @link http://php-console.com
* @author Sergey Barbushin http://linkedin.com/in/barbushin
* @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
Expand Down
2 changes: 1 addition & 1 deletion src/PhpConsole/Dispatcher/Debug.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Sends debug data to connector as client expected messages
*
* @package PhpConsole
* @version 3.0
* @version 3.1
* @link http://php-console.com
* @author Sergey Barbushin http://linkedin.com/in/barbushin
* @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
Expand Down
2 changes: 1 addition & 1 deletion src/PhpConsole/Dispatcher/Errors.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Sends system errors and exceptions to connector as client expected messages
*
* @package PhpConsole
* @version 3.0
* @version 3.1
* @link http://php-console.com
* @author Sergey Barbushin http://linkedin.com/in/barbushin
* @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
Expand Down
2 changes: 1 addition & 1 deletion src/PhpConsole/Dispatcher/Evaluate.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Executes client code and sends result data to connector as client expected messages
*
* @package PhpConsole
* @version 3.0
* @version 3.1
* @link http://php-console.com
* @author Sergey Barbushin http://linkedin.com/in/barbushin
* @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
Expand Down
2 changes: 1 addition & 1 deletion src/PhpConsole/Dumper.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Convert any type of var to string or array with different kind of limits
*
* @package PhpConsole
* @version 3.0
* @version 3.1
* @link http://php-console.com
* @author Sergey Barbushin http://linkedin.com/in/barbushin
* @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
Expand Down
2 changes: 1 addition & 1 deletion src/PhpConsole/EvalProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Execute PHP code with some security & accessibility tweaks
*
* @package PhpConsole
* @version 3.0
* @version 3.1
* @link http://php-console.com
* @author Sergey Barbushin http://linkedin.com/in/barbushin
* @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
Expand Down
2 changes: 1 addition & 1 deletion src/PhpConsole/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* https://chrome.google.com/webstore/detail/php-console/nfhmhhlpfleoednkpnnnkolmclajemef
*
* @package PhpConsole
* @version 3.0
* @version 3.1
* @link http://php-console.com
* @author Sergey Barbushin http://linkedin.com/in/barbushin
* @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
Expand Down
2 changes: 1 addition & 1 deletion src/PhpConsole/Helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* It will be the same as calling Handler::getInstance()->debug($var, 'db')
*
* @package PhpConsole
* @version 3.0
* @version 3.1
* @link http://php-console.com
* @author Sergey Barbushin http://linkedin.com/in/barbushin
* @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
Expand Down
2 changes: 1 addition & 1 deletion src/PhpConsole/OldVersionAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
* 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.0
* @version 3.1
* @link http://php-console.com
* @author Sergey Barbushin http://linkedin.com/in/barbushin
* @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
Expand Down
2 changes: 1 addition & 1 deletion src/PhpConsole/PsrLogger.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* IMPORTANT: https://github.com/php-fig/log must be installed & autoloaded
*
* @package PhpConsole
* @version 3.0
* @version 3.1
* @link http://php-console.com
* @author Sergey Barbushin http://linkedin.com/in/barbushin
* @copyright © Sergey Barbushin, 2011-2013. All rights reserved.
Expand Down
40 changes: 40 additions & 0 deletions src/PhpConsole/Storage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?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;
}
}
Loading

0 comments on commit 22f87b9

Please sign in to comment.