<commit_before><?php
/*
This class implements a Predis connection that actually talks with Webdis
(http://github.com/nicolasff/webdis) instead of connecting directly to Redis.
It relies on the http PECL extension to communicate with the web server and the
phpiredis extension to parse the protocol of the replies returned in the http
response bodies.

Since this connection class is highly experimental, some features have not been
implemented yet (or they simply cannot be implemented at all). Here is a list:

  - Pipelining commands.
  - Publish / Subscribe.
  - MULTI / EXEC transactions (not yet supported by Webdis).
*/

namespace Predis\Network;

use HttpRequest;
use Predis\IConnectionParameters;
use Predis\ResponseError;
use Predis\ClientException;
use Predis\ServerException;
use Predis\CommunicationException;
use Predis\Commands\ICommand;

const ERR_MSG_EXTENSION = 'The %s extension must be loaded in order to be able to use this connection class';

class WebdisConnection implements IConnectionSingle {
    private $_parameters;
    private $_webdisUrl;
    private $_reader;

    private static function throwNotImplementedException($class, $function) {
        throw new \RuntimeException("The method $class::$function() is not implemented");
    }

    public function __construct(IConnectionParameters $parameters) {
        $this->checkExtensions();
        if ($parameters->scheme !== 'http') {
            throw new \InvalidArgumentException("Invalid scheme: {$parameters->scheme}");
        }
        $this->_parameters = $parameters;
        $this->_webdisUrl = "{$parameters->scheme}://{$parameters->host}:{$parameters->port}";
        $this->_reader = $this->initializeReader($parameters);
    }

    public function __destruct() {
        phpiredis_reader_destroy($this->_reader);
    }

    private function checkExtensions() {
        if (!class_exists("HttpRequest")) {
            throw new ClientException(sprintf(ERR_MSG_EXTENSION, 'http'));
        }
        if (!function_exists('phpiredis_reader_create')) {
            throw new ClientException(sprintf(ERR_MSG_EXTENSION, 'phpiredis'));
        }
    }

    private function initializeReader(IConnectionParameters $parameters) {
        $throwErrors = $parameters->throw_errors;
        $reader = phpiredis_reader_create();
        phpiredis_reader_set_status_handler($reader, $this->getStatusHandler());
        phpiredis_reader_set_error_handler($reader, $this->getErrorHandler($throwErrors));
        return $reader;
    }

    private function getStatusHandler() {
        return function($payload) {
            return $payload === 'OK' ? true : $payload;
        };
    }

    private function getErrorHandler($throwErrors) {
        if ($throwErrors) {
            return function($errorMessage) {
                throw new ServerException($errorMessage);
            };
        }
        return function($errorMessage) {
            return new ResponseError($errorMessage);
        };
    }

    private static function argumentsSerializer($str, $arg) {
        $str .= '/' . urlencode($arg);
        return $str;
    }

    public function connect() {
        // NOOP
    }

    public function disconnect() {
        // NOOP
    }

    public function isConnected() {
        return true;
    }

    public function writeCommand(ICommand $command) {
        self::throwNotImplementedException(__CLASS__, __FUNCTION__);
    }

    public function readResponse(ICommand $command) {
        self::throwNotImplementedException(__CLASS__, __FUNCTION__);
    }

    public function executeCommand(ICommand $command) {
        $params = $this->_parameters;
        $arguments = array_reduce($command->getArguments(), 'self::argumentsSerializer');

        $request = new HttpRequest($this->_webdisUrl, HttpRequest::METH_POST);
        $request->setBody(sprintf('%s%s.raw', $command->getId(), $arguments));
        $request->send();

        phpiredis_reader_feed($this->_reader, $request->getResponseBody());
        $reply = phpiredis_reader_get_reply($this->_reader);
        return isset($reply->skipParse) ? $reply : $command->parseResponse($reply);
    }

    public function getResource() {
        self::throwNotImplementedException(__CLASS__, __FUNCTION__);
    }

    public function getParameters() {
        return $this->_parameters;
    }

    public function pushInitCommand(ICommand $command) {
        self::throwNotImplementedException(__CLASS__, __FUNCTION__);
    }

    public function read() {
        self::throwNotImplementedException(__CLASS__, __FUNCTION__);
    }

    public function __toString() {
        return "{$this->_parameters->host}:{$this->_parameters->port}";
    }
}<commit_msg>Use a faster approach to serialize the POST data for HTTP requests to Webdis.<commit_after><?php
/*
This class implements a Predis connection that actually talks with Webdis
(http://github.com/nicolasff/webdis) instead of connecting directly to Redis.
It relies on the http PECL extension to communicate with the web server and the
phpiredis extension to parse the protocol of the replies returned in the http
response bodies.

Since this connection class is highly experimental, some features have not been
implemented yet (or they simply cannot be implemented at all). Here is a list:

  - Pipelining commands.
  - Publish / Subscribe.
  - MULTI / EXEC transactions (not yet supported by Webdis).
*/

namespace Predis\Network;

use HttpRequest;
use Predis\IConnectionParameters;
use Predis\ResponseError;
use Predis\ClientException;
use Predis\ServerException;
use Predis\CommunicationException;
use Predis\Commands\ICommand;

const ERR_MSG_EXTENSION = 'The %s extension must be loaded in order to be able to use this connection class';

class WebdisConnection implements IConnectionSingle {
    private $_parameters;
    private $_webdisUrl;
    private $_reader;

    private static function throwNotImplementedException($class, $function) {
        throw new \RuntimeException("The method $class::$function() is not implemented");
    }

    public function __construct(IConnectionParameters $parameters) {
        $this->checkExtensions();
        if ($parameters->scheme !== 'http') {
            throw new \InvalidArgumentException("Invalid scheme: {$parameters->scheme}");
        }
        $this->_parameters = $parameters;
        $this->_webdisUrl = "{$parameters->scheme}://{$parameters->host}:{$parameters->port}";
        $this->_reader = $this->initializeReader($parameters);
    }

    public function __destruct() {
        phpiredis_reader_destroy($this->_reader);
    }

    private function checkExtensions() {
        if (!class_exists("HttpRequest")) {
            throw new ClientException(sprintf(ERR_MSG_EXTENSION, 'http'));
        }
        if (!function_exists('phpiredis_reader_create')) {
            throw new ClientException(sprintf(ERR_MSG_EXTENSION, 'phpiredis'));
        }
    }

    private function initializeReader(IConnectionParameters $parameters) {
        $throwErrors = $parameters->throw_errors;
        $reader = phpiredis_reader_create();
        phpiredis_reader_set_status_handler($reader, $this->getStatusHandler());
        phpiredis_reader_set_error_handler($reader, $this->getErrorHandler($throwErrors));
        return $reader;
    }

    private function getStatusHandler() {
        return function($payload) {
            return $payload === 'OK' ? true : $payload;
        };
    }

    private function getErrorHandler($throwErrors) {
        if ($throwErrors) {
            return function($errorMessage) {
                throw new ServerException($errorMessage);
            };
        }
        return function($errorMessage) {
            return new ResponseError($errorMessage);
        };
    }

    public function connect() {
        // NOOP
    }

    public function disconnect() {
        // NOOP
    }

    public function isConnected() {
        return true;
    }

    public function writeCommand(ICommand $command) {
        self::throwNotImplementedException(__CLASS__, __FUNCTION__);
    }

    public function readResponse(ICommand $command) {
        self::throwNotImplementedException(__CLASS__, __FUNCTION__);
    }

    public function executeCommand(ICommand $command) {
        $arguments = implode('/', array_map('urlencode', $command->getArguments()));

        $request = new HttpRequest($this->_webdisUrl, HttpRequest::METH_POST);
        $request->setBody("{$command->getId()}/$arguments.raw");
        $request->send();

        phpiredis_reader_feed($this->_reader, $request->getResponseBody());

        $reply = phpiredis_reader_get_reply($this->_reader);
        if ($reply instanceof IReplyObject) {
            return $reply;
        }
        return $command->parseResponse($reply);
    }

    public function getResource() {
        self::throwNotImplementedException(__CLASS__, __FUNCTION__);
    }

    public function getParameters() {
        return $this->_parameters;
    }

    public function pushInitCommand(ICommand $command) {
        self::throwNotImplementedException(__CLASS__, __FUNCTION__);
    }

    public function read() {
        self::throwNotImplementedException(__CLASS__, __FUNCTION__);
    }

    public function __toString() {
        return "{$this->_parameters->host}:{$this->_parameters->port}";
    }
}
{'fraction_non_alphanumeric': 0.10781584582441113, 'fraction_numerical': 0.0, 'mean_word_length': 3.2731015553522416, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 61, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4809597', 'n_tokens_mistral': 2430, 'n_tokens_neox': 2391, 'n_words': 619}