diff --git a/src/Handlers/ShellMessagesHandler.php b/src/Handlers/ShellMessagesHandler.php index cb25a9f..9ba60c8 100644 --- a/src/Handlers/ShellMessagesHandler.php +++ b/src/Handlers/ShellMessagesHandler.php @@ -19,6 +19,7 @@ use JupyterPHP\Shell; use JupyterPHP\KernelOutput; +use JupyterPHP\zPHP; use Monolog\Logger; use React\ZMQ\SocketWrapper; @@ -33,7 +34,7 @@ final class ShellMessagesHandler /** @var KernelInfoAction */ private $kernelInfoAction; - /** @var \JupyterPHP\Actions\ShutdownAction */ + /** @var ShutdownAction */ private $shutdownAction; /** @var Shell */ @@ -42,6 +43,9 @@ final class ShellMessagesHandler /** @var Logger */ private $logger; + /** @var zPHP */ + private $zPHP; + public function __construct( JupyterBroker $broker, @@ -50,8 +54,9 @@ public function __construct( Logger $logger ) { $this->shellSoul = new Shell(); + $this->zPHP = new zPHP($broker,$iopubSocket); - $this->executeAction = new ExecuteAction($broker, $iopubSocket, $shellSocket, $this->shellSoul); + $this->executeAction = new ExecuteAction($broker, $iopubSocket, $shellSocket, $this->shellSoul, $this->zPHP); $this->historyAction = new HistoryAction($broker, $shellSocket); $this->kernelInfoAction = new KernelInfoAction($broker, $shellSocket, $iopubSocket); $this->shutdownAction = new ShutdownAction($broker, $iopubSocket, $shellSocket); @@ -70,20 +75,20 @@ public function __invoke(array $msg) // Read ZMQ IDs until we reach the delimiter $zmqIds = array(); while (!empty($msg)) { - $item = \array_shift($msg); + $item = array_shift($msg); if ($item === '') break; - else \array_push($zmqIds, $item); + else array_push($zmqIds, $item); } // Read the remaining items list($hmac, $header, $parentHeader, $metadata, $content) = $msg; - $header = \json_decode($header, true); - $content = \json_decode($content, true); + $header = json_decode($header, true); + $content = json_decode($content, true); $this->logger->debug('Received message', [ - 'processId' => \getmypid(), - 'zmqIds' => \htmlentities(\implode(", ", $zmqIds), ENT_COMPAT, "UTF-8"), + 'processId' => getmypid(), + 'zmqIds' => htmlentities(implode(", ", $zmqIds), ENT_COMPAT, "UTF-8"), 'hmac' => $hmac, 'header' => $header, 'parentHeader' => $parentHeader, @@ -102,7 +107,7 @@ public function __invoke(array $msg) } elseif ('comm_open' === $header['msg_type']) { // TODO: Research about what should be done. } else { - $this->logger->error('Unknown message type', ['processId' => \getmypid(), 'header' => $header]); + $this->logger->error('Unknown message type', ['processId' => getmypid(), 'header' => $header]); } } } diff --git a/src/zPHP.php b/src/zPHP.php new file mode 100644 index 0000000..adb8642 --- /dev/null +++ b/src/zPHP.php @@ -0,0 +1,141 @@ + + * + * based on https://jupyter-client.readthedocs.io/en/latest/messaging.html + */ + +namespace JupyterPHP; + +use React\ZMQ\SocketWrapper; + +class zPHP +{ + /** @var JupyterBroker */ + private $broker; + + /** @var SocketWrapper */ + private $iopubSocket; + + private $header; + + private $pluginVersion; + + public function __construct($broker,$iopubSocket) + { + $this->broker = $broker; + $this->iopubSocket = $iopubSocket; + + $this->reloadPlugins(); + } + public function send($msgType, $data) + { + $this->broker->send($this->iopubSocket, $msgType, $data, $this->header); + } + + public function setHeader($header) + { + $this->header = $header; + } + + public function reloadPlugins() + { + $this->pluginVersion = str_replace('.','',microtime(true)); + } + + private function getObject($name) + { + $namespace = str_replace('.','','SpecialDimension_'.$this->pluginVersion); + $className = "{$namespace}\\{$name}"; + + if(!class_exists($className)) + { + $fileName = __DIR__."/zPHP/{$name}.php"; + $classBody = "namespace {$namespace}; ?>".file_get_contents($fileName); + + eval($classBody); + } + + $className = "{$namespace}\\{$name}"; + $object = new $className($this); + return $object; + } + + public function __call($className,$params) + { + $object = $this->getObject($className); + + call_user_func_array($object,$params); + } + + public function imageWorks($imageData) + { + $image = imagecreatefromstring($imageData); + + $width = imagesx($image); + $height = imagesy($image); + + ob_start(); + imagepng($image); + $imageData = ob_get_clean(); + + $this->send( + 'display_data', + [ + 'data'=>[ + 'image/png'=>base64_encode($imageData) + ], + 'metadata'=>[ + 'image/png'=>[ + 'width'=>$width, + 'height'=>$height + ] + ] + ] + ); + } + + public function htmlWorks($htmlData) + { + $this->send( + 'display_data', + [ + 'data'=>[ + 'text/html'=>$htmlData + ], + 'metadata'=>[] + ] + ); + } + + public function imageDummy() + { + $this->send( + 'display_data', + [ + 'data'=>[ + 'image/gif'=>'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' + ], + 'metadata'=>[ + 'image/gif'=>[ + 'width'=>64, + 'height'=>48 + ] + ] + ] + ); + } + public function vegaDummy($mime) + { + $this->send( + 'display_data', + [ + 'data'=>[ + $mime=>file_get_contents('a1.vega') + ], + 'metadata'=>[] + ] + ); + } +} diff --git a/src/zPHP/HTML.php b/src/zPHP/HTML.php new file mode 100644 index 0000000..3221db1 --- /dev/null +++ b/src/zPHP/HTML.php @@ -0,0 +1,32 @@ + + */ + +use JupyterPHP\zPHP; + +class HTML +{ + /** @var JupyterPHP\zPHP */ + private $zPHP; + + public function __construct(zPHP $zPHP) + { + $this->zPHP = $zPHP; + } + public function __invoke($htmlData) + { + $this->zPHP->send( + 'display_data', + [ + 'data'=>[ + 'text/html'=>$htmlData + ], + 'metadata'=>[] + ] + ); + } + + +} diff --git a/src/zPHP/Image.php b/src/zPHP/Image.php new file mode 100644 index 0000000..79a1519 --- /dev/null +++ b/src/zPHP/Image.php @@ -0,0 +1,45 @@ + + */ + +use JupyterPHP\zPHP; + +class Image +{ + /** @var JupyterPHP\zPHP */ + private $zPHP; + + public function __construct(zPHP $zPHP) + { + $this->zPHP = $zPHP; + } + + public function __invoke($imageData) + { + $image = imagecreatefromstring($imageData); + + $width = imagesx($image); + $height = imagesy($image); + + ob_start(); + imagepng($image); + $imageData = ob_get_clean(); + + $this->zPHP->send( + 'display_data', + [ + 'data'=>[ + 'image/png'=>base64_encode($imageData) + ], + 'metadata'=>[ + 'image/png'=>[ + 'width'=>$width, + 'height'=>$height + ] + ] + ] + ); + } +} diff --git a/src/zPHP/Vega.php b/src/zPHP/Vega.php new file mode 100644 index 0000000..3ac52ad --- /dev/null +++ b/src/zPHP/Vega.php @@ -0,0 +1,46 @@ + + */ + +use JupyterPHP\zPHP; + +class Vega +{ + /** @var \Litipk\JupyterPHP\zPHP */ + private $zPHP; + + public function __construct(zPHP $zPHP) + { + $this->zPHP = $zPHP; + } + public function __invoke() + { + $this->zPHP->send( + 'display_data', + [ + 'data'=>[ + "application/vnd.vegalite.v2+json"=>json_decode('{ + "$schema": "https://vega.github.io/schema/vega-lite/v2.json", + "description": "A simple bar chart with embedded data.", + "data": { + "values": [ + {"a": "A", "b": 28}, {"a": "B", "b": 55}, {"a": "C", "b": 43}, + {"a": "D", "b": 91}, {"a": "E", "b": 81}, {"a": "F", "b": 53}, + {"a": "G", "b": 91}, {"a": "H", "b": 87}, {"a": "I", "b": 52} + ] + }, + "mark": "bar", + "encoding": { + "x": {"field": "a", "type": "ordinal"}, + "y": {"field": "b", "type": "quantitative"} + } + }'), + "text/plain"=> " If you see this message, it means the renderer has not been properly enabled for the frontend that you are using. For more information, see https://altair-viz.github.io/user_guide/troubleshooting.html " + ], + 'metadata'=>[] + ] + ); + } +}