From 132167b814352f3b8a811062671d47c07ea76d73 Mon Sep 17 00:00:00 2001 From: basakest <47746206+basakest@users.noreply.github.com> Date: Thu, 9 Sep 2021 19:13:01 +0800 Subject: [PATCH] feat: Implement keyGet() and keyGet2() built-in method, fix #97 (#103) --- src/Model/FunctionMap.php | 6 ++ src/Util/BuiltinOperations.php | 93 +++++++++++++++++++++++ tests/Unit/Util/BuiltinOperationsTest.php | 57 ++++++++++++++ 3 files changed, 156 insertions(+) diff --git a/src/Model/FunctionMap.php b/src/Model/FunctionMap.php index 5d1c219..9612d31 100644 --- a/src/Model/FunctionMap.php +++ b/src/Model/FunctionMap.php @@ -41,9 +41,15 @@ public static function loadFunctionMap(): self $fm->addFunction('keyMatch', function (...$args) { return BuiltinOperations::keyMatchFunc(...$args); }); + $fm->addFunction('keyGet', function (...$args) { + return BuiltinOperations::keyGetFunc(...$args); + }); $fm->addFunction('keyMatch2', function (...$args) { return BuiltinOperations::keyMatch2Func(...$args); }); + $fm->addFunction('keyGet2', function (...$args) { + return BuiltinOperations::keyGet2Func(...$args); + }); $fm->addFunction('regexMatch', function (...$args) { return BuiltinOperations::regexMatchFunc(...$args); }); diff --git a/src/Util/BuiltinOperations.php b/src/Util/BuiltinOperations.php index 22d9370..25990d2 100644 --- a/src/Util/BuiltinOperations.php +++ b/src/Util/BuiltinOperations.php @@ -52,6 +52,43 @@ public static function keyMatchFunc(...$args): bool return self::keyMatch($name1, $name2); } + /** + * KeyGet returns the matched part + * For example, "/foo/bar/foo" matches "/foo/*" + * "bar/foo" will been returned + * + * @param string $key1 + * @param string $key2 + * @return string + */ + public static function keyGet(string $key1, string $key2): string + { + $i = strpos($key2, '*'); + if ($i === false) { + return ""; + } + if (strlen($key1) > $i) { + if (substr($key1, 0, $i) == substr($key2, 0, $i)) { + return substr($key1, $i); + } + } + return ''; + } + + /** + * KeyGetFunc is the wrapper for KeyGet + * + * @param mixed ...$args + * @return string + */ + public static function keyGetFunc(...$args) + { + $name1 = $args[0]; + $name2 = $args[1]; + + return self::keyGet($name1, $name2); + } + /** * Determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *. * For example, "/foo/bar" matches "/foo/*", "/resource1" matches "/:resource". @@ -96,6 +133,62 @@ public static function keyMatch2Func(...$args): bool return self::keyMatch2($name1, $name2); } + /** + * KeyGet2 returns value matched pattern + * For example, "/resource1" matches "/:resource" + * if the pathVar == "resource", then "resource1" will be returned + * + * @param string $key1 + * @param string $key2 + * @param string $pathVar + * @return string + */ + public static function keyGet2(string $key1, string $key2, string $pathVar): string + { + $key2 = str_replace(['/*'], ['/.*'], $key2); + + $pattern = '/:[^\/]+/'; + $keys = []; + preg_match_all($pattern, $key2, $keys); + $keys = $keys[0]; + $key2 = preg_replace_callback( + $pattern, + function ($m) { + return '([^\/]+)'; + }, + $key2 + ); + + $key2 = "~^" . $key2 . "$~"; + $values = []; + preg_match($key2, $key1, $values); + + if (count($values) === 0) { + return ''; + } + foreach ($keys as $i => $key) { + if ($pathVar == substr($key, 1)) { + return $values[$i + 1]; + } + } + return ''; + } + + /** + * KeyGet2Func is the wrapper for KeyGet2 + * + * @param mixed ...$args + * @return string + */ + public static function keyGet2Func(...$args) + { + $name1 = $args[0]; + $name2 = $args[1]; + $key = $args[2]; + + return self::keyGet2($name1, $name2, $key); + } + /** * Determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *. * For example, "/foo/bar" matches "/foo/*", "/resource1" matches "/{resource}". diff --git a/tests/Unit/Util/BuiltinOperationsTest.php b/tests/Unit/Util/BuiltinOperationsTest.php index 091b0d7..9f495a3 100644 --- a/tests/Unit/Util/BuiltinOperationsTest.php +++ b/tests/Unit/Util/BuiltinOperationsTest.php @@ -37,6 +37,16 @@ private function globMatchFunc($name1, $name2) return BuiltinOperations::globMatchFunc($name1, $name2); } + public function keyGetFunc($name1, $name2) + { + return BuiltinOperations::KeyGetFunc($name1, $name2); + } + + public function keyGet2Func($name1, $name2, $pathVar) + { + return BuiltinOperations::KeyGet2Func($name1, $name2, $pathVar); + } + public function testKeyMatchFunc() { $this->assertTrue($this->keyMatchFunc('/foo', '/foo')); @@ -136,4 +146,51 @@ public function testGlobMatchFunc() $this->assertTrue($this->globMatchFunc('/foo/bar', '/foo/*')); } + + public function testKeyGetFunc() + { + $this->assertEquals('', $this->keyGetFunc('/foo', '/foo')); + $this->assertEquals('', $this->keyGetFunc('/foo', '/foo*')); + $this->assertEquals('', $this->keyGetFunc('/foo', '/foo/*')); + $this->assertEquals('', $this->keyGetFunc('/foo/bar', '/foo')); + $this->assertEquals('/bar', $this->keyGetFunc('/foo/bar', '/foo*')); + $this->assertEquals('bar', $this->keyGetFunc('/foo/bar', '/foo/*')); + $this->assertEquals('', $this->keyGetFunc('/foobar', '/foo')); + $this->assertEquals('bar', $this->keyGetFunc('/foobar', '/foo*')); + $this->assertEquals('', $this->keyGetFunc('/foobar', '/foo/*')); + } + + public function testKeyGet2Func() + { + $this->assertEquals('', $this->keyGet2Func("/foo", "/foo", "id")); + $this->assertEquals('', $this->keyGet2Func("/foo", "/foo*", "id")); + $this->assertEquals('', $this->keyGet2Func("/foo", "/foo/*", "id")); + $this->assertEquals('', $this->keyGet2Func("/foo/bar", "/foo", "id")); + // different with KeyMatch. + $this->assertEquals('', $this->keyGet2Func("/foo/bar", "/foo*", "id")); + $this->assertEquals('', $this->keyGet2Func("/foo/bar", "/foo/*", "id")); + $this->assertEquals('', $this->keyGet2Func("/foobar", "/foo", "id")); + // different with KeyMatch. + $this->assertEquals('', $this->keyGet2Func("/foobar", "/foo*", "id")); + $this->assertEquals('', $this->keyGet2Func("/foobar", "/foo/*", "id")); + $this->assertEquals('', $this->keyGet2Func("/", "/:resource", "resource")); + $this->assertEquals('resource1', $this->keyGet2Func("/resource1", "/:resource", "resource")); + $this->assertEquals('', $this->keyGet2Func("/myid", "/:id/using/:resId", "id")); + $this->assertEquals('myid', $this->keyGet2Func("/myid/using/myresid", "/:id/using/:resId", "id")); + $this->assertEquals('myresid', $this->keyGet2Func("/myid/using/myresid", "/:id/using/:resId", "resId")); + + $this->assertEquals('', $this->keyGet2Func("/proxy/myid", "/proxy/:id/*", "id")); + $this->assertEquals('myid', $this->keyGet2Func("/proxy/myid/", "/proxy/:id/*", "id")); + $this->assertEquals('myid', $this->keyGet2Func("/proxy/myid/res", "/proxy/:id/*", "id")); + $this->assertEquals('myid', $this->keyGet2Func("/proxy/myid/res/res2", "/proxy/:id/*", "id")); + $this->assertEquals('myid', $this->keyGet2Func("/proxy/myid/res/res2/res3", "/proxy/:id/*", "id")); + $this->assertEquals('myid', $this->keyGet2Func("/proxy/myid/res/res2/res3", "/proxy/:id/res/*", "id")); + $this->assertEquals('', $this->keyGet2Func('/proxy/', "/proxy/:id/*", "id")); + $this->assertEquals('alice', $this->keyGet2Func("/alice", "/:id", "id")); + $this->assertEquals('alice', $this->keyGet2Func("/alice/all", "/:id/all", "id")); + $this->assertEquals('', $this->keyGet2Func("/alice", "/:id/all", "id")); + $this->assertEquals('', $this->keyGet2Func("/alice/all", "/:id", "id")); + + $this->assertEquals('', $this->keyGet2Func("/alice/all", "/:/all", "")); + } }