From 1e88220aad73cb323dbf13e8b64e650801c7d1f7 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Tue, 14 Dec 2021 16:38:00 -0800 Subject: [PATCH] Civi::pipe - Add top-level facade. Add E2E test. --- Civi.php | 9 ++++++ Civi/Core/Container.php | 10 ++++++ tests/phpunit/E2E/Extern/CliRunnerTest.php | 37 ++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/Civi.php b/Civi.php index f087d19c8d..b1e3e44eab 100644 --- a/Civi.php +++ b/Civi.php @@ -114,6 +114,15 @@ class Civi { return new Civi\Core\Format(); } + /** + * Initiate a bidirectional pipe for exchanging a series of multiple API requests. + * + * @see \Civi\Pipe\BasicJsonSession + */ + public static function pipe(string $protocol = 'jsonrpc20'): void { + Civi::service('pipe.' . $protocol)->setIO(STDIN, STDOUT)->run(); + } + /** * Fetch a service from the container. * diff --git a/Civi/Core/Container.php b/Civi/Core/Container.php index d733b6ce04..acc712fe61 100644 --- a/Civi/Core/Container.php +++ b/Civi/Core/Container.php @@ -262,6 +262,16 @@ class Container { } $container->setAlias('cache.short', 'cache.default')->setPublic(TRUE); + $container->setDefinition('pipe.json', new Definition( + 'Civi\Pipe\BasicJsonSession', + [] + ))->setPublic(TRUE); + + $container->setDefinition('pipe.jsonrpc20', new Definition( + 'Civi\Pipe\JsonRpcSession', + [] + ))->setPublic(TRUE); + $container->setDefinition('resources', new Definition( 'CRM_Core_Resources', [new Reference('service_container')] diff --git a/tests/phpunit/E2E/Extern/CliRunnerTest.php b/tests/phpunit/E2E/Extern/CliRunnerTest.php index db1bf239cb..42cb6fa4d6 100644 --- a/tests/phpunit/E2E/Extern/CliRunnerTest.php +++ b/tests/phpunit/E2E/Extern/CliRunnerTest.php @@ -110,6 +110,43 @@ class E2E_Extern_CliRunnerTest extends CiviEndToEndTestCase { ); } + /** + * Use a CLI runner to start a bidirectional command pipe. + * + * This ensures that there are no funny headers or constraints of bidirectional data-flow. + * + * @param string $name + * The name of the command we're testing with. + * Ex: 'cv' + * @param string $runner + * Ex: 'cv ev @PHP' + * @dataProvider getRunners + */ + public function testPipe($name, $runner) { + $cmd = strtr($runner, ['@PHP' => escapeshellarg('Civi::pipe("jsonrpc20");')]); + $desc = [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'a']]; + $process = proc_open($cmd, $desc, $pipes); + + $write = function(string $method, array $data = []) use (&$pipes) { + fwrite($pipes[0], json_encode(['jsonrpc' => '2.0', 'method' => $method, 'params' => $data, 'id' => NULL]) . "\n"); + }; + $read = function() use (&$pipes) { + $line = stream_get_line($pipes[1], 4096, "\n"); + $decode = json_decode($line, TRUE); + return $decode; + }; + + $this->assertEquals(['Civi::pipe' => ['jsonrpc20']], $read(), "Expect standard Civi::pipe header when starting via $name"); + + $write('echo', ['a' => 123]); + $this->assertEquals(['a' => 123], $read()['result']); + + $write('echo', [4, 5, 6]); + $this->assertEquals([4, 5, 6], $read()['result']); + + proc_close($process); + } + /** * @param string $runner * Ex: 'cv ev @PHP' -- 2.25.1