--- /dev/null
+<?php
+
+namespace Graphit\Graph\Client;
+
+use Graphit\Graph\Common\ConnectionInterface;
+
+class Connection extends RemoteConnection implements ConnectionInterface {
+
+ /** @var string */
+ protected $url;
+
+ /** @var resource */
+ protected $socket;
+
+ /** @var string */
+ protected $protocoll;
+
+ /** @var string */
+ protected $cryptoType;
+
+ public function __construct($url, array $options = [])
+ {
+ $this->url = $url;
+ $this->options = $options;
+
+ $matches = [];
+ $pattern = '/^(?<protocoll>[^:]+):\/\//';
+ if (preg_match($pattern, $this->url, $matches) !== 1) {
+ throw new \Exception('Unable to find Transport in URL '.$this->url.'!');
+ }
+ $this->protocoll = $matches['protocoll'];
+
+ $this->cryptoType = false;
+ $cryptoTypes = [
+ 'tls' => STREAM_CRYPTO_METHOD_TLS_CLIENT,
+ ];
+ if (isset($cryptoTypes[$this->protocoll])) {
+ $this->cryptoType = $cryptoTypes[$this->protocoll];
+ }
+ }
+
+ public function __destruct()
+ {
+ if ($this->socket) {
+ fclose($this->socket);
+ }
+ }
+
+ protected function _connect()
+ {
+ $context = stream_context_create();
+
+ if ($this->cryptoType) {
+ stream_context_set_option($context, [
+ 'ssl' => [
+ 'verify_peer' => true,
+ 'verify_peer_name' => false,
+ 'ciphers' => 'HIGH',
+ 'cafile' => $this->options['cafile'],
+ 'local_cert' => $this->options['lofile'],
+ ],
+ ]);
+ }
+
+ $this->socket = stream_socket_client($this->url, $errno, $errstr, ini_get('default_socket_timeout'), STREAM_CLIENT_CONNECT, $context);
+ if ($this->socket === false) {
+ throw new \Exception("stream_socket_server() failed: $errstr\n");
+ }
+ $this->_readMessage();
+ }
+
+ protected function _readBytes($size) {
+ $bytes = '';
+ while (strlen($bytes) < $size) {
+ $fread = fread($this->socket, $size - strlen($bytes));
+ if ($fread === false) {
+ break;
+ }
+ $bytes.= $fread;
+ }
+ if (strlen($bytes) != $size) {
+ throw new \Exception("Unable to read $size Bytes from the socket");
+ }
+ return $bytes;
+ }
+
+ protected function _readMessage() {
+ $size = $this->_readBytes(4);
+ $size = unpack('Vsize', $size)['size'];
+
+ $message = $this->_readBytes($size);
+ return msgpack_unpack($message);
+ }
+
+ protected function _writeBytes($bytes) {
+ $size = strlen($bytes);
+ for ($written = 0; $written < strlen($bytes); $written += $fwrite) {
+ $fwrite = fwrite($this->socket, substr($bytes, $written));
+ if ($fwrite === false) {
+ break;
+ }
+ }
+ if ($written != $size) {
+ throw new \Exception("Unable to write $size Bytes request to socket");
+ }
+ }
+
+ protected function _writeMessage($message) {
+ $message = msgpack_pack($message);
+ $message = pack('V', strlen($message)).$message;
+ $this->_writeBytes($message);
+ }
+
+ /** */
+ protected function call($method, array $params)
+ {
+ static $id = 0;
+
+ if ( !$this->socket) {
+ $this->_connect();
+ }
+
+ $request = [
+ 'jsonrpc' => '2.0',
+ 'method' => $method,
+ 'params' => $params,
+ 'id' => ++$id,
+ ];
+ $this->_writeMessage($request);
+
+ $response = $this->_readMessage();
+ if ( !isset($response['jsonrpc']) || $response['jsonrpc'] != $request['jsonrpc']) {
+ throw new \Exception('Not a JSON-RPC 2.0 response!');
+ }
+ if (isset($response['error'])) {
+ $error = implode(' ', $response['error']);
+ throw new \Exception("JSON-RPC: Remote error: {$error}");
+ }
+ if ( !isset($response['id']) || $response['id'] != $request['id']) {
+ throw new \Exception('JSON-RPC id missing or invalid');
+ }
+
+ return $response['result'];
+ }
+}
--- /dev/null
+<?php
+
+namespace Graphit\Graph\Client;
+
+use Graphit\Graph\Common\ConnectionInterface;
+
+/**
+ * Die RemoteConnection ist die Basis-Klasse für ConnectionInterface
+ * Implementationen, die mit einem entfernten Graphen kommunizieren. All Aufrufe
+ * auf Methoden des ConnectionInterfaces werden an die abstrakte Methode
+ * RemoteConnection::call() weitergeleitet.
+ *
+ * @author Sebastian Wassen <sebastian.wassen@graph-it.org>
+ */
+abstract class RemoteConnection implements ConnectionInterface
+{
+ /**
+ * Leitet den Methodenaufruf an den Graphen weiter.
+ *
+ * @param string $method die aufgerufene Methode
+ * @param array $params die Paremeter der Methode
+ *
+ * @return mixed|null die Antwort vom Graphen
+ */
+ protected abstract function call($method, array $params);
+
+ /** */
+ public function erzeuge($knoten_typ)
+ {
+ return $this->call('erzeuge', array($knoten_typ));
+ }
+
+ /** */
+ public function vernichte($node_guid)
+ {
+ return $this->call('vernichte', array($node_guid));
+ }
+
+ /** */
+ public function knotentyp($node_guid)
+ {
+ return $this->call('knotentyp', array($node_guid));
+ }
+
+ /** */
+ public function setze($node_guid, $attributknoten_typ, $wert)
+ {
+ return $this->call('setze', array($node_guid, $attributknoten_typ, $wert));
+ }
+
+ /** */
+ public function attribut($node_guid, $attributknoten_typ)
+ {
+ return $this->call('attribut', array($node_guid, $attributknoten_typ));
+ }
+
+ /** */
+ public function attribute($node_guid, $knoten_typ, $attribute)
+ {
+ return $this->call('attribute', array($node_guid, $knoten_typ, $attribute));
+ }
+
+ /** */
+ public function attributsknoten($attributknoten_typ, $wert)
+ {
+ return $this->call('attributsknoten', array($attributknoten_typ, $wert));
+ }
+
+ /** */
+ public function berechne($node_guid, $datenfunktion_name)
+ {
+ return $this->call('berechne', array($node_guid, $datenfunktion_name));
+ }
+
+ /** */
+ public function verknuepfe($node_guid1, $node_guid2)
+ {
+ return $this->call('verknuepfe', array($node_guid1, $node_guid2));
+ }
+
+ /** */
+ public function entknuepfe($node_guid1, $node_guid2)
+ {
+ return $this->call('entknuepfe', array($node_guid1, $node_guid2));
+ }
+
+ /** */
+ public function knoten($node_guid, $knoten_typ)
+ {
+ return $this->call('knoten', array($node_guid, $knoten_typ));
+ }
+
+ /** */
+ public function liste($node_guid, $knoten_typ, $reihenfolge = false, $eingrenzung = false, array $parameter = array())
+ {
+ return $this->call('liste', array($node_guid, $knoten_typ, $reihenfolge, $eingrenzung, $parameter));
+ }
+
+ /** */
+ public function listeattribute($node_guid, $knoten_typ, $attribute, $reihenfolge = false, $eingrenzung = false, array $parameter = array())
+ {
+ return $this->call('listeattribute', array($node_guid, $knoten_typ, $attribute, $reihenfolge, $eingrenzung, $parameter));
+ }
+
+ /** */
+ public function listezahl($node_guid, $knoten_typ, $eingrenzung = false, array $parameter = array())
+ {
+ return $this->call('listezahl', array($node_guid, $knoten_typ, $eingrenzung, $parameter));
+ }
+
+ /** */
+ public function moegliche($node_guid, $knoten_typ, $reihenfolge = false, $eingrenzung = false, array $parameter = array())
+ {
+ return $this->call('moegliche', array($node_guid, $knoten_typ, $reihenfolge, $eingrenzung, $parameter));
+ }
+
+ /** */
+ public function moeglicheattribute($node_guid, $knoten_typ, $attribute, $reihenfolge = false, $eingrenzung = false, array $parameter = array())
+ {
+ return $this->call('moeglicheattribute', array($node_guid, $knoten_typ, $attribute, $reihenfolge, $eingrenzung, $parameter));
+ }
+
+ /** */
+ public function moeglichezahl($node_guid, $knoten_typ, $eingrenzung = false, array $parameter = array())
+ {
+ return $this->call('moeglichezahl', array($node_guid, $knoten_typ, $eingrenzung, $parameter));
+ }
+
+ /** */
+ public function alle($knoten_typ, $reihenfolge = false, $eingrenzung = false, array $parameter = array())
+ {
+ return $this->call('alle', array($knoten_typ, $reihenfolge, $eingrenzung, $parameter));
+ }
+
+ /** */
+ public function alleattribute($knoten_typ, $attribute, $reihenfolge = false, $eingrenzung = false, array $parameter = array())
+ {
+ return $this->call('alleattribute', array($knoten_typ, $attribute, $reihenfolge, $eingrenzung, $parameter));
+ }
+
+ /** */
+ public function allezahl($knoten_typ, $eingrenzung = false, array $parameter = array())
+ {
+ return $this->call('allezahl', array($knoten_typ, $eingrenzung, $parameter));
+ }
+
+ /** */
+ public function aktion($node_guid, $aktionfunktion_name)
+ {
+ return $this->call('aktion', array($node_guid, $aktionfunktion_name));
+ }
+
+ /** */
+ public function erzeugeknoten($knoten_typ)
+ {
+ return $this->call('erzeugeknoten', array($knoten_typ));
+ }
+
+ /** */
+ public function vernichteknoten($node_guid)
+ {
+ return $this->call('vernichteknoten', array($node_guid));
+ }
+
+ /** */
+ public function service($graphmodulservice_name, ...$parameter)
+ {
+ return $this->call('service', func_get_args());
+ }
+
+ /** */
+ public function sessionattribut($schluessel, $default = null)
+ {
+ return $this->call('sessionattribut',[$schluessel, $default]);
+ }
+
+ /** */
+ public function sessionsetze($schluessel, $wert)
+ {
+ return $this->call('sessionsetze', [$schluessel, $wert]);
+ }
+ /* */
+}