From caa8f106913122fdd393e668736adb823d189bb0 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Wed, 5 Jul 2017 17:52:56 +0300 Subject: [PATCH 01/32] add phpunit --- bin/test.sh | 2 ++ composer.json | 3 ++- phpunit.xml | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 phpunit.xml diff --git a/bin/test.sh b/bin/test.sh index d500f9307..0bea7c1e9 100755 --- a/bin/test.sh +++ b/bin/test.sh @@ -2,6 +2,8 @@ set -ex +phpunit + # Run the functional tests BEHAT_TAGS=$(php utils/behat-tags.php) behat --format progress $BEHAT_TAGS --strict diff --git a/composer.json b/composer.json index 8b4f4959d..3833916d1 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,8 @@ "require": {}, "require-dev": { "behat/behat": "~2.5", - "wp-cli/wp-cli": "*" + "wp-cli/wp-cli": "*", + "phpunit/phpunit": "^4.8" }, "extra": { "branch-alias": { diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 000000000..1b43955e8 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,21 @@ + + + + tests + + + + + src + + + From 124b5a9ae0231926609294f163b10924d3f68277 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Wed, 5 Jul 2017 18:02:55 +0300 Subject: [PATCH 02/32] add RecursiveDataStructureTraverser + tests --- .../RecursiveDataStructureTraverser.php | 153 ++++++++++++++++++ tests/RecursiveDataStructureTraverserTest.php | 120 ++++++++++++++ 2 files changed, 273 insertions(+) create mode 100644 src/WP_CLI/RecursiveDataStructureTraverser.php create mode 100644 tests/RecursiveDataStructureTraverserTest.php diff --git a/src/WP_CLI/RecursiveDataStructureTraverser.php b/src/WP_CLI/RecursiveDataStructureTraverser.php new file mode 100644 index 000000000..1b462b483 --- /dev/null +++ b/src/WP_CLI/RecursiveDataStructureTraverser.php @@ -0,0 +1,153 @@ +data =& $data; + $this->parent = $parent; + } + + /** + * @param $locator + * + * @throws \Exception + * + * @return static + */ + public function get( $locator ) { + return $this->traverse_to( $locator )->value(); + } + + /** + * Get the current data. + * + * @return mixed + */ + public function value() { + return $this->data; + } + + public function set( $locator, $value ) { + $this->traverse_to( $locator )->set_value( $value ); + } + + public function set_value( $value ) { + $this->data = $value; + } + + public function delete( $locator ) { + $this->traverse_to( $locator )->unset_on_parent(); + } + + /** + * Delete the key on the parent's data that references this data. + */ + public function unset_on_parent() { + $this->parent->delete_by_key( $this->key ); + } + + /** + * Delete the given key from the data. + * + * @param $key + */ + public function delete_by_key( $key ) { + if ( is_array( $this->data ) ) { + unset( $this->data[ $key ] ); + } else { + unset( $this->data->$key ); + } + } + + /** + * Get an instance of the traverser for the given hierarchical key. + * + * @param $locator + * + * @throws \Exception + * + * @return static + */ + public function traverse_to( $locator ) { + if ( 0 < strlen( $locator ) ) { + list( $current, $locator ) = $this->parse_locator( $locator ); + + if ( ! $this->exists( $current ) ) { + throw new \Exception( "No data exists for $current \n " . print_r( $this->data, true ) ); + } + + foreach ( $this->data as $key => &$key_data ) { + if ( $key === $current ) { + $traverser = new static( $key_data, $this ); + $traverser->set_key( $key ); + $traverser->set_delimiter( $this->delimiter ); + return $traverser->traverse_to( $locator ); + } + } + } + + return $this; + } + + public function set_key( $key ) { + $this->key = $key; + } + + public function set_delimiter( $delimiter ) { + $this->delimiter = $delimiter; + } + + /** + * Check if the given key exists on the current data. + * + * @param string $key + * + * @return bool + */ + public function exists( $key ) { + return ( is_array( $this->data ) && array_key_exists( $key, $this->data ) ) || ( is_object( $this->data ) && property_exists( $this->data, $key ) ); + } + + protected function parse_locator( $locator ) { + $parsed = array( + 'current' => $locator, + 'locator' => false, + ); + + if ( 0 < strlen( $this->delimiter ) ) { + $segments = explode( $this->delimiter, $locator, 2 ); + $parsed['current'] = array_shift( $segments ); + $parsed['locator'] = array_shift( $segments ); + } + + return array_values( $parsed ); + } +} \ No newline at end of file diff --git a/tests/RecursiveDataStructureTraverserTest.php b/tests/RecursiveDataStructureTraverserTest.php new file mode 100644 index 000000000..137d20be2 --- /dev/null +++ b/tests/RecursiveDataStructureTraverserTest.php @@ -0,0 +1,120 @@ + 'bar', + ); + + $traverser = new RecursiveDataStructureTraverser( $array ); + + $this->assertEquals( 'bar', $traverser->get( 'foo' ) ); + } + + /** @test */ + function it_can_get_a_top_level_object_value() { + $object = (object) array( + 'foo' => 'bar', + ); + + $traverser = new RecursiveDataStructureTraverser( $object ); + + $this->assertEquals( 'bar', $traverser->get( 'foo' ) ); + } + + /** @test */ + function it_can_get_a_nested_array_value() { + $array = array( + 'foo' => array( + 'bar' => 'baz', + ), + ); + + $traverser = new RecursiveDataStructureTraverser( $array ); + $traverser->set_delimiter( '.' ); + + $this->assertEquals( 'baz', $traverser->get( 'foo.bar' ) ); + } + + /** @test */ + function it_can_get_a_nested_object_value() { + $object = (object) array( + 'foo' => (object) array( + 'bar' => 'baz', + ), + ); + + $traverser = new RecursiveDataStructureTraverser( $object ); + $traverser->set_delimiter( '.' ); + + $this->assertEquals( 'baz', $traverser->get( 'foo.bar' ) ); + } + + /** @test */ + function it_can_set_a_nested_array_value() { + $array = array( + 'foo' => array( + 'bar' => 'baz', + ), + ); + $this->assertEquals( 'baz', $array['foo']['bar'] ); + + $traverser = new RecursiveDataStructureTraverser( $array ); + $traverser->set_delimiter( '.' ); + $traverser->set( 'foo.bar', 'new' ); + + $this->assertEquals( 'new', $array['foo']['bar'] ); + } + + /** @test */ + function it_can_set_a_nested_object_value() { + $object = (object) array( + 'foo' => (object) array( + 'bar' => 'baz', + ), + ); + $this->assertEquals( 'baz', $object->foo->bar ); + + $traverser = new RecursiveDataStructureTraverser( $object ); + $traverser->set_delimiter( '.' ); + $traverser->set( 'foo.bar', 'new' ); + + $this->assertEquals( 'new', $object->foo->bar ); + } + + /** @test */ + function it_can_unset_a_nested_array_value() { + $array = array( + 'foo' => array( + 'bar' => 'baz', + ), + ); + $this->assertArrayHasKey( 'bar', $array['foo'] ); + + $traverser = new RecursiveDataStructureTraverser( $array ); + $traverser->set_delimiter( '.' ); + $traverser->delete( 'foo.bar' ); + + $this->assertArrayNotHasKey( 'bar', $array['foo'] ); + } + + /** @test */ + function it_can_unset_a_nested_object_value() { + $object = (object) array( + 'foo' => (object) array( + 'bar' => 'baz', + ), + ); + $this->assertObjectHasAttribute( 'bar', $object->foo ); + + $traverser = new RecursiveDataStructureTraverser( $object ); + $traverser->set_delimiter( '.' ); + $traverser->delete( 'foo.bar' ); + + $this->assertObjectNotHasAttribute( 'bar', $object->foo ); + } +} From 195d100a50c467c98495ea51e9973a2901a22dea Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Wed, 5 Jul 2017 18:03:23 +0300 Subject: [PATCH 03/32] add initial pluck + patch to meta commands --- features/post-meta.feature | 92 +++++++++++++++++++++++++ src/WP_CLI/CommandWithMeta.php | 121 +++++++++++++++++++++++++++++++++ 2 files changed, 213 insertions(+) diff --git a/features/post-meta.feature b/features/post-meta.feature index e8dd92da3..c84e36f66 100644 --- a/features/post-meta.feature +++ b/features/post-meta.feature @@ -139,3 +139,95 @@ Feature: Manage post custom fields """ My\New\Meta """ + + @pluck + Scenario: Multi-dimensional values can be plucked. + Given a WP install + And an input.json file: + """ + { + "foo": "bar" + } + """ + And I run `wp post meta set 1 meta-key --format=json < input.json` + + When I run `wp post meta pluck 1 meta-key foo` + Then STDOUT should be: + """ + bar + """ + + @pluck @pluck-deep + Scenario: Multi-dimensional values can be plucked at any depth. + Given a WP install + And an input.json file: + """ + { + "foo": { + "bar": { + "baz": "some value" + } + }, + "foo.com": { + "visitors": 999 + } + } + """ + And I run `wp post meta set 1 meta-key --format=json < input.json` + + When I run `wp post meta pluck 1 meta-key foo.bar.baz` + Then STDOUT should be: + """ + some value + """ + + When I run `wp post meta pluck 1 meta-key foo@bar@baz --delimiter=@` + Then STDOUT should be: + """ + some value + """ + + When I run `wp post meta pluck 1 meta-key foo.com@visitors --delimiter=@` + Then STDOUT should be: + """ + 999 + """ + + @pluck @pluck-fail + Scenario: The command fails when attempting to pluck a non-existent nested value. + Given a WP install + And I run `wp post meta set 1 meta-key '{ "key": "value" }' --format=json` + + When I run `wp post meta pluck 1 meta-key key` + Then STDOUT should be: + """ + value + """ + + When I try `wp post meta pluck 1 meta-key foo` + Then STDOUT should be empty + + @patch + Scenario: Multi-dimensional values can be patched. + Given a WP install + And an input.json file: + """ + { + "foo": "bar" + } + """ + And I run `wp post meta set 1 meta-key --format=json < input.json` + + When I run `wp post meta patch 1 meta-key foo baz` + Then STDOUT should be: + """ + Success: Updated custom field 'meta-key'. + """ + + When I run `wp post meta get 1 meta-key` + Then STDOUT should be: + """ + array ( + 'foo' => 'baz', + ) + """ \ No newline at end of file diff --git a/src/WP_CLI/CommandWithMeta.php b/src/WP_CLI/CommandWithMeta.php index d1224eaf9..ba8068dc1 100644 --- a/src/WP_CLI/CommandWithMeta.php +++ b/src/WP_CLI/CommandWithMeta.php @@ -253,6 +253,127 @@ public function update( $args, $assoc_args ) { } + /** + * Get meta field value. + * + * ## OPTIONS + * + * + * : The ID of the object. + * + * + * : The name of the meta field to get. + * + * + * : The name of the inner key to get. + * + * [--delimiter=] + * : The character to delimit hierarchy within the pluck-key. + * --- + * default: . + * --- + * + * [--format=] + * : Accepted values: table, json. Default: table + * + */ + public function pluck( $args, $assoc_args ) { + list( $object_id, $meta_key, $pluck_key ) = $args; + + $object_id = $this->check_object_id( $object_id ); + + $value = get_metadata( $this->meta_type, $object_id, $meta_key, true ); + + $traverser = new RecursiveDataStructureTraverser( $value ); + $traverser->set_delimiter( $assoc_args['delimiter'] ); + + try { + $value = $traverser->get( $pluck_key ); + } catch ( \Exception $e ) { + die( 1 ); + } + + WP_CLI::print_value( $value, $assoc_args ); + } + + /** + * Update a multi-dimensional meta field. + * + * ## OPTIONS + * + * + * : The ID of the object. + * + * + * : The name of the meta field to update. + * + * + * : The name of the inner key to update. + * + * [] + * : The new value. If omitted, the value is read from STDIN. + * + * [--delimiter=] + * : The character to delimit hierarchy within the patch-key. + * --- + * default: . + * --- + * + * [--mode=] + * : Configures the behavior of the value update. + * --- + * default: replace + * options: + * - replace + * - unset + * --- + * + * [--format=] + * : The serialization format for the value. + * --- + * default: plaintext + * options: + * - plaintext + * - json + * --- + */ + public function patch( $args, $assoc_args ) { + list( $object_id, $meta_key, $patch_key ) = $args; + + $patch_value = WP_CLI::get_value_from_arg_or_stdin( $args, 3 ); + $patch_value = WP_CLI::read_value( $patch_value, $assoc_args ); + + $object_id = $this->check_object_id( $object_id ); + + /* Need to make a copy of $current_meta_value here as it is modified by reference */ + $current_meta_value = $old_meta_value = sanitize_meta( $meta_key, get_metadata( $this->meta_type, $object_id, $meta_key, true ), $this->meta_type ); + + $traverser = new RecursiveDataStructureTraverser( $current_meta_value ); + $traverser->set_delimiter( $assoc_args['delimiter'] ); + + $method = 'replace' == $assoc_args['mode'] ? 'set' : 'delete'; + try { + $traverser->$method( $patch_key, $patch_value ); + } catch ( \Exception $e ) { + WP_CLI::error( $e->getMessage() ); + } + + $patched_meta_value = sanitize_meta( $meta_key, $traverser->value(), $this->meta_type ); + + if ( $patched_meta_value === $old_meta_value ) { + WP_CLI::success( "Value passed for custom field '$meta_key' is unchanged." ); + } else { + $slashed = wp_slash( $patched_meta_value ); + $success = update_metadata( $this->meta_type, $object_id, $meta_key, $slashed ); + + if ( $success ) { + WP_CLI::success( "Updated custom field '$meta_key'." ); + } else { + WP_CLI::error( "Failed to update custom field '$meta_key'." ); + } + } + } + /** * Get the fields for this object's meta * From b71936b6059c00f477b37908cecd83ab314bf7c4 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Sun, 9 Jul 2017 12:54:31 +0300 Subject: [PATCH 04/32] remove concept of a delimiter, convert to array of keys --- features/post-meta.feature | 10 ++----- src/WP_CLI/CommandWithMeta.php | 13 --------- .../RecursiveDataStructureTraverser.php | 28 +++---------------- tests/RecursiveDataStructureTraverserTest.php | 22 ++++++--------- 4 files changed, 15 insertions(+), 58 deletions(-) diff --git a/features/post-meta.feature b/features/post-meta.feature index c84e36f66..a9e121cdb 100644 --- a/features/post-meta.feature +++ b/features/post-meta.feature @@ -175,19 +175,13 @@ Feature: Manage post custom fields """ And I run `wp post meta set 1 meta-key --format=json < input.json` - When I run `wp post meta pluck 1 meta-key foo.bar.baz` + When I run `wp post meta pluck 1 meta-key foo bar baz` Then STDOUT should be: """ some value """ - When I run `wp post meta pluck 1 meta-key foo@bar@baz --delimiter=@` - Then STDOUT should be: - """ - some value - """ - - When I run `wp post meta pluck 1 meta-key foo.com@visitors --delimiter=@` + When I run `wp post meta pluck 1 meta-key foo.com visitors` Then STDOUT should be: """ 999 diff --git a/src/WP_CLI/CommandWithMeta.php b/src/WP_CLI/CommandWithMeta.php index ba8068dc1..44328f676 100644 --- a/src/WP_CLI/CommandWithMeta.php +++ b/src/WP_CLI/CommandWithMeta.php @@ -267,11 +267,6 @@ public function update( $args, $assoc_args ) { * * : The name of the inner key to get. * - * [--delimiter=] - * : The character to delimit hierarchy within the pluck-key. - * --- - * default: . - * --- * * [--format=] * : Accepted values: table, json. Default: table @@ -285,7 +280,6 @@ public function pluck( $args, $assoc_args ) { $value = get_metadata( $this->meta_type, $object_id, $meta_key, true ); $traverser = new RecursiveDataStructureTraverser( $value ); - $traverser->set_delimiter( $assoc_args['delimiter'] ); try { $value = $traverser->get( $pluck_key ); @@ -313,12 +307,6 @@ public function pluck( $args, $assoc_args ) { * [] * : The new value. If omitted, the value is read from STDIN. * - * [--delimiter=] - * : The character to delimit hierarchy within the patch-key. - * --- - * default: . - * --- - * * [--mode=] * : Configures the behavior of the value update. * --- @@ -349,7 +337,6 @@ public function patch( $args, $assoc_args ) { $current_meta_value = $old_meta_value = sanitize_meta( $meta_key, get_metadata( $this->meta_type, $object_id, $meta_key, true ), $this->meta_type ); $traverser = new RecursiveDataStructureTraverser( $current_meta_value ); - $traverser->set_delimiter( $assoc_args['delimiter'] ); $method = 'replace' == $assoc_args['mode'] ? 'set' : 'delete'; try { diff --git a/src/WP_CLI/RecursiveDataStructureTraverser.php b/src/WP_CLI/RecursiveDataStructureTraverser.php index 1b462b483..92c9f7c46 100644 --- a/src/WP_CLI/RecursiveDataStructureTraverser.php +++ b/src/WP_CLI/RecursiveDataStructureTraverser.php @@ -43,7 +43,7 @@ public function __construct( &$data, $parent = null ) { * @return static */ public function get( $locator ) { - return $this->traverse_to( $locator )->value(); + return $this->traverse_to( (array) $locator )->value(); } /** @@ -56,7 +56,7 @@ public function value() { } public function set( $locator, $value ) { - $this->traverse_to( $locator )->set_value( $value ); + $this->traverse_to( (array) $locator )->set_value( $value ); } public function set_value( $value ) { @@ -97,8 +97,8 @@ public function delete_by_key( $key ) { * @return static */ public function traverse_to( $locator ) { - if ( 0 < strlen( $locator ) ) { - list( $current, $locator ) = $this->parse_locator( $locator ); + if ( is_array( $locator ) && count( $locator ) ) { + $current = array_shift( $locator ); if ( ! $this->exists( $current ) ) { throw new \Exception( "No data exists for $current \n " . print_r( $this->data, true ) ); @@ -108,7 +108,6 @@ public function traverse_to( $locator ) { if ( $key === $current ) { $traverser = new static( $key_data, $this ); $traverser->set_key( $key ); - $traverser->set_delimiter( $this->delimiter ); return $traverser->traverse_to( $locator ); } } @@ -121,10 +120,6 @@ public function set_key( $key ) { $this->key = $key; } - public function set_delimiter( $delimiter ) { - $this->delimiter = $delimiter; - } - /** * Check if the given key exists on the current data. * @@ -135,19 +130,4 @@ public function set_delimiter( $delimiter ) { public function exists( $key ) { return ( is_array( $this->data ) && array_key_exists( $key, $this->data ) ) || ( is_object( $this->data ) && property_exists( $this->data, $key ) ); } - - protected function parse_locator( $locator ) { - $parsed = array( - 'current' => $locator, - 'locator' => false, - ); - - if ( 0 < strlen( $this->delimiter ) ) { - $segments = explode( $this->delimiter, $locator, 2 ); - $parsed['current'] = array_shift( $segments ); - $parsed['locator'] = array_shift( $segments ); - } - - return array_values( $parsed ); - } } \ No newline at end of file diff --git a/tests/RecursiveDataStructureTraverserTest.php b/tests/RecursiveDataStructureTraverserTest.php index 137d20be2..c45a7d5db 100644 --- a/tests/RecursiveDataStructureTraverserTest.php +++ b/tests/RecursiveDataStructureTraverserTest.php @@ -30,14 +30,15 @@ function it_can_get_a_top_level_object_value() { function it_can_get_a_nested_array_value() { $array = array( 'foo' => array( - 'bar' => 'baz', + 'bar' => array( + 'baz' => 'value' + ), ), ); $traverser = new RecursiveDataStructureTraverser( $array ); - $traverser->set_delimiter( '.' ); - $this->assertEquals( 'baz', $traverser->get( 'foo.bar' ) ); + $this->assertEquals( 'value', $traverser->get( array( 'foo', 'bar', 'baz' ) ) ); } /** @test */ @@ -49,9 +50,8 @@ function it_can_get_a_nested_object_value() { ); $traverser = new RecursiveDataStructureTraverser( $object ); - $traverser->set_delimiter( '.' ); - $this->assertEquals( 'baz', $traverser->get( 'foo.bar' ) ); + $this->assertEquals( 'baz', $traverser->get( array( 'foo', 'bar' ) ) ); } /** @test */ @@ -64,8 +64,7 @@ function it_can_set_a_nested_array_value() { $this->assertEquals( 'baz', $array['foo']['bar'] ); $traverser = new RecursiveDataStructureTraverser( $array ); - $traverser->set_delimiter( '.' ); - $traverser->set( 'foo.bar', 'new' ); + $traverser->set( array( 'foo', 'bar' ), 'new' ); $this->assertEquals( 'new', $array['foo']['bar'] ); } @@ -80,8 +79,7 @@ function it_can_set_a_nested_object_value() { $this->assertEquals( 'baz', $object->foo->bar ); $traverser = new RecursiveDataStructureTraverser( $object ); - $traverser->set_delimiter( '.' ); - $traverser->set( 'foo.bar', 'new' ); + $traverser->set( array( 'foo', 'bar' ), 'new' ); $this->assertEquals( 'new', $object->foo->bar ); } @@ -96,8 +94,7 @@ function it_can_unset_a_nested_array_value() { $this->assertArrayHasKey( 'bar', $array['foo'] ); $traverser = new RecursiveDataStructureTraverser( $array ); - $traverser->set_delimiter( '.' ); - $traverser->delete( 'foo.bar' ); + $traverser->delete( array( 'foo', 'bar' ) ); $this->assertArrayNotHasKey( 'bar', $array['foo'] ); } @@ -112,8 +109,7 @@ function it_can_unset_a_nested_object_value() { $this->assertObjectHasAttribute( 'bar', $object->foo ); $traverser = new RecursiveDataStructureTraverser( $object ); - $traverser->set_delimiter( '.' ); - $traverser->delete( 'foo.bar' ); + $traverser->delete( array( 'foo', 'bar' ) ); $this->assertObjectNotHasAttribute( 'bar', $object->foo ); } From b4073eab25e04d8fe27651b72613938b506bf4e3 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Sun, 9 Jul 2017 12:55:39 +0300 Subject: [PATCH 05/32] add assertion about return code for a failed pluck --- features/post-meta.feature | 1 + 1 file changed, 1 insertion(+) diff --git a/features/post-meta.feature b/features/post-meta.feature index a9e121cdb..6750d72fa 100644 --- a/features/post-meta.feature +++ b/features/post-meta.feature @@ -200,6 +200,7 @@ Feature: Manage post custom fields When I try `wp post meta pluck 1 meta-key foo` Then STDOUT should be empty + And the return code should be 1 @patch Scenario: Multi-dimensional values can be patched. From 2b011e9c03dbdc205aff767661fb1f797796c716 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Sun, 9 Jul 2017 12:57:23 +0300 Subject: [PATCH 06/32] rename a few scenarios --- features/post-meta.feature | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/features/post-meta.feature b/features/post-meta.feature index 6750d72fa..2461dc95c 100644 --- a/features/post-meta.feature +++ b/features/post-meta.feature @@ -141,7 +141,7 @@ Feature: Manage post custom fields """ @pluck - Scenario: Multi-dimensional values can be plucked. + Scenario: Nested values can be retrieved. Given a WP install And an input.json file: """ @@ -158,7 +158,7 @@ Feature: Manage post custom fields """ @pluck @pluck-deep - Scenario: Multi-dimensional values can be plucked at any depth. + Scenario: A nested value can be retrieved at any depth. Given a WP install And an input.json file: """ @@ -188,7 +188,7 @@ Feature: Manage post custom fields """ @pluck @pluck-fail - Scenario: The command fails when attempting to pluck a non-existent nested value. + Scenario: Attempting to pluck a non-existent nested value fails. Given a WP install And I run `wp post meta set 1 meta-key '{ "key": "value" }' --format=json` From 670a7a45c63962d1802ee405486db773c1c3d5a0 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Sun, 9 Jul 2017 13:03:27 +0300 Subject: [PATCH 07/32] update out format options for pluck --- src/WP_CLI/CommandWithMeta.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/WP_CLI/CommandWithMeta.php b/src/WP_CLI/CommandWithMeta.php index 44328f676..2c909379d 100644 --- a/src/WP_CLI/CommandWithMeta.php +++ b/src/WP_CLI/CommandWithMeta.php @@ -269,8 +269,13 @@ public function update( $args, $assoc_args ) { * * * [--format=] - * : Accepted values: table, json. Default: table - * + * : The output format of the value. + * --- + * default: plaintext + * options: + * - plaintext + * - json + * - yaml */ public function pluck( $args, $assoc_args ) { list( $object_id, $meta_key, $pluck_key ) = $args; From 2fcf1ed9e76085690ebe207bdabf8f36a7cde3a3 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Sun, 9 Jul 2017 13:08:14 +0300 Subject: [PATCH 08/32] pass key path to traverser as array in pluck cmd --- src/WP_CLI/CommandWithMeta.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/WP_CLI/CommandWithMeta.php b/src/WP_CLI/CommandWithMeta.php index 2c909379d..cd30a7b86 100644 --- a/src/WP_CLI/CommandWithMeta.php +++ b/src/WP_CLI/CommandWithMeta.php @@ -264,9 +264,8 @@ public function update( $args, $assoc_args ) { * * : The name of the meta field to get. * - * - * : The name of the inner key to get. - * + * ... + * : The name(s) of the keys within the value to locate the value to pluck. * * [--format=] * : The output format of the value. @@ -278,16 +277,16 @@ public function update( $args, $assoc_args ) { * - yaml */ public function pluck( $args, $assoc_args ) { - list( $object_id, $meta_key, $pluck_key ) = $args; - + list( $object_id, $meta_key ) = $args; $object_id = $this->check_object_id( $object_id ); + $key_path = array_slice( $args, 2 ); $value = get_metadata( $this->meta_type, $object_id, $meta_key, true ); $traverser = new RecursiveDataStructureTraverser( $value ); try { - $value = $traverser->get( $pluck_key ); + $value = $traverser->get( $key_path ); } catch ( \Exception $e ) { die( 1 ); } From 3da5eccd1d41a1d22b5204c0b0a871032399bd54 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Sun, 9 Jul 2017 13:10:25 +0300 Subject: [PATCH 09/32] add another scenario for testing pluck failure --- features/post-meta.feature | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/features/post-meta.feature b/features/post-meta.feature index 2461dc95c..ff32d8243 100644 --- a/features/post-meta.feature +++ b/features/post-meta.feature @@ -202,8 +202,17 @@ Feature: Manage post custom fields Then STDOUT should be empty And the return code should be 1 - @patch - Scenario: Multi-dimensional values can be patched. + @pluck @pluck-fail + Scenario: Attempting to pluck from a primitive value fails. + Given a WP install + And I run `wp post meta set 1 meta-key simple-value` + + When I try `wp post meta pluck 1 meta-key foo` + Then STDOUT should be empty + And the return code should be 1 + + @patch @patch-arg + Scenario: Nested values can be changed. Given a WP install And an input.json file: """ From 5acaf20234c147b0f151b2761447ec25dec3855b Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Sun, 9 Jul 2017 13:33:03 +0300 Subject: [PATCH 10/32] update patch command and scenarios --- features/post-meta.feature | 67 +++++++++++++++++++++++++++++++--- src/WP_CLI/CommandWithMeta.php | 24 ++++++++---- 2 files changed, 79 insertions(+), 12 deletions(-) diff --git a/features/post-meta.feature b/features/post-meta.feature index ff32d8243..62f6c5d1b 100644 --- a/features/post-meta.feature +++ b/features/post-meta.feature @@ -228,10 +228,67 @@ Feature: Manage post custom fields Success: Updated custom field 'meta-key'. """ - When I run `wp post meta get 1 meta-key` + When I run `wp post meta get 1 meta-key --format=json` + Then STDOUT should be JSON containing: + """ + { + "foo": "baz" + } + """ + + @patch @patch-stdin + Scenario: Nested values can be set with a value from STDIN. + Given a WP install + And an input.json file: + """ + { + "foo": { + "bar": "baz" + }, + "bar": "bad" + } + """ + And a patch file: + """ + new value + """ + And I run `wp post meta set 1 meta-key --format=json < input.json` + + When I run `wp post meta patch 1 meta-key foo bar < patch` Then STDOUT should be: """ - array ( - 'foo' => 'baz', - ) - """ \ No newline at end of file + Success: Updated custom field 'meta-key'. + """ + + When I run `wp post meta get 1 meta-key --format=json` + Then STDOUT should be JSON containing: + """ + { + "foo": { + "bar": "new value" + }, + "bar": "bad" + } + """ + + @patch @patch-fail + Scenario: Attempting to set a nested value fails if a parent's key does not exist. + Given a WP install + And an input.json file: + """ + { + "foo": { + "bar": "baz" + }, + "bar": "bad" + } + """ + And I run `wp post meta set 1 meta-key --format=json < input.json` + + When I try `wp post meta patch 1 meta-key foo no-key no-key 'new-value'` + Then STDOUT should be empty + And STDERR should contain: + """ + No data exists for no-key + """ + And the return code should be 1 diff --git a/src/WP_CLI/CommandWithMeta.php b/src/WP_CLI/CommandWithMeta.php index cd30a7b86..3a615672d 100644 --- a/src/WP_CLI/CommandWithMeta.php +++ b/src/WP_CLI/CommandWithMeta.php @@ -305,8 +305,8 @@ public function pluck( $args, $assoc_args ) { * * : The name of the meta field to update. * - * - * : The name of the inner key to update. + * ... + * : The name(s) of the keys within the value to locate the value to patch. * * [] * : The new value. If omitted, the value is read from STDIN. @@ -330,12 +330,22 @@ public function pluck( $args, $assoc_args ) { * --- */ public function patch( $args, $assoc_args ) { - list( $object_id, $meta_key, $patch_key ) = $args; + list( $object_id, $meta_key ) = $args; + $object_id = $this->check_object_id( $object_id ); + $key_path = array_slice( $args, 2 ); - $patch_value = WP_CLI::get_value_from_arg_or_stdin( $args, 3 ); - $patch_value = WP_CLI::read_value( $patch_value, $assoc_args ); + /** + * We need to check STDIN first as we have no way of determining the index of the value. + * We set the read from STDIN to non-blocking, or it will be stuck in an infinite loop if not passed. + */ + stream_set_blocking( STDIN, 0 ); + $stdin_value = trim( WP_CLI::get_value_from_arg_or_stdin( $args, -1 ) ); - $object_id = $this->check_object_id( $object_id ); + if ( '' !== $stdin_value ) { + $patch_value = WP_CLI::read_value( $stdin_value, $assoc_args ); + } else { + $patch_value = WP_CLI::read_value( array_pop( $key_path ), $assoc_args ); + } /* Need to make a copy of $current_meta_value here as it is modified by reference */ $current_meta_value = $old_meta_value = sanitize_meta( $meta_key, get_metadata( $this->meta_type, $object_id, $meta_key, true ), $this->meta_type ); @@ -344,7 +354,7 @@ public function patch( $args, $assoc_args ) { $method = 'replace' == $assoc_args['mode'] ? 'set' : 'delete'; try { - $traverser->$method( $patch_key, $patch_value ); + $traverser->$method( $key_path, $patch_value ); } catch ( \Exception $e ) { WP_CLI::error( $e->getMessage() ); } From b178f7bbb20d8b3e777d1cc03476f7793e7a140f Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Sun, 9 Jul 2017 13:33:16 +0300 Subject: [PATCH 11/32] reword command descriptions --- src/WP_CLI/CommandWithMeta.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WP_CLI/CommandWithMeta.php b/src/WP_CLI/CommandWithMeta.php index 3a615672d..123b77a9a 100644 --- a/src/WP_CLI/CommandWithMeta.php +++ b/src/WP_CLI/CommandWithMeta.php @@ -254,7 +254,7 @@ public function update( $args, $assoc_args ) { } /** - * Get meta field value. + * Get a nested value from a meta field. * * ## OPTIONS * @@ -295,7 +295,7 @@ public function pluck( $args, $assoc_args ) { } /** - * Update a multi-dimensional meta field. + * Update a nested value for a meta field. * * ## OPTIONS * From 05ca479af016c817bfc711621916064800fd8ba0 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Sat, 15 Jul 2017 23:42:03 +0300 Subject: [PATCH 12/32] replace patch mode flag with action arg --- features/post-meta.feature | 16 ++++++------ src/WP_CLI/CommandWithMeta.php | 25 +++++++++---------- .../RecursiveDataStructureTraverser.php | 2 +- tests/RecursiveDataStructureTraverserTest.php | 4 +-- 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/features/post-meta.feature b/features/post-meta.feature index 62f6c5d1b..a76c9494c 100644 --- a/features/post-meta.feature +++ b/features/post-meta.feature @@ -211,7 +211,7 @@ Feature: Manage post custom fields Then STDOUT should be empty And the return code should be 1 - @patch @patch-arg + @patch @patch-update @patch-arg Scenario: Nested values can be changed. Given a WP install And an input.json file: @@ -222,7 +222,7 @@ Feature: Manage post custom fields """ And I run `wp post meta set 1 meta-key --format=json < input.json` - When I run `wp post meta patch 1 meta-key foo baz` + When I run `wp post meta patch update 1 meta-key foo baz` Then STDOUT should be: """ Success: Updated custom field 'meta-key'. @@ -236,7 +236,7 @@ Feature: Manage post custom fields } """ - @patch @patch-stdin + @patch @patch-update @patch-stdin Scenario: Nested values can be set with a value from STDIN. Given a WP install And an input.json file: @@ -254,7 +254,7 @@ Feature: Manage post custom fields """ And I run `wp post meta set 1 meta-key --format=json < input.json` - When I run `wp post meta patch 1 meta-key foo bar < patch` + When I run `wp post meta patch update 1 meta-key foo bar < patch` Then STDOUT should be: """ Success: Updated custom field 'meta-key'. @@ -271,8 +271,8 @@ Feature: Manage post custom fields } """ - @patch @patch-fail - Scenario: Attempting to set a nested value fails if a parent's key does not exist. + @patch @patch-update @patch-fail + Scenario: Attempting to update a nested value fails if a parent's key does not exist. Given a WP install And an input.json file: """ @@ -285,10 +285,10 @@ Feature: Manage post custom fields """ And I run `wp post meta set 1 meta-key --format=json < input.json` - When I try `wp post meta patch 1 meta-key foo no-key no-key 'new-value'` + When I try `wp post meta patch update 1 meta-key foo not-a-key new-value` Then STDOUT should be empty And STDERR should contain: """ - No data exists for no-key + No data exists for not-a-key """ And the return code should be 1 diff --git a/src/WP_CLI/CommandWithMeta.php b/src/WP_CLI/CommandWithMeta.php index 123b77a9a..f5497719e 100644 --- a/src/WP_CLI/CommandWithMeta.php +++ b/src/WP_CLI/CommandWithMeta.php @@ -299,6 +299,15 @@ public function pluck( $args, $assoc_args ) { * * ## OPTIONS * + * + * : Patch action to perform. + * --- + * options: + * - add + * - update + * - remove + * --- + * * * : The ID of the object. * @@ -311,15 +320,6 @@ public function pluck( $args, $assoc_args ) { * [] * : The new value. If omitted, the value is read from STDIN. * - * [--mode=] - * : Configures the behavior of the value update. - * --- - * default: replace - * options: - * - replace - * - unset - * --- - * * [--format=] * : The serialization format for the value. * --- @@ -330,9 +330,9 @@ public function pluck( $args, $assoc_args ) { * --- */ public function patch( $args, $assoc_args ) { - list( $object_id, $meta_key ) = $args; + list( $action, $object_id, $meta_key ) = $args; $object_id = $this->check_object_id( $object_id ); - $key_path = array_slice( $args, 2 ); + $key_path = array_slice( $args, 3 ); /** * We need to check STDIN first as we have no way of determining the index of the value. @@ -352,9 +352,8 @@ public function patch( $args, $assoc_args ) { $traverser = new RecursiveDataStructureTraverser( $current_meta_value ); - $method = 'replace' == $assoc_args['mode'] ? 'set' : 'delete'; try { - $traverser->$method( $key_path, $patch_value ); + $traverser->$action( $key_path, $patch_value ); } catch ( \Exception $e ) { WP_CLI::error( $e->getMessage() ); } diff --git a/src/WP_CLI/RecursiveDataStructureTraverser.php b/src/WP_CLI/RecursiveDataStructureTraverser.php index 92c9f7c46..ee98c1a56 100644 --- a/src/WP_CLI/RecursiveDataStructureTraverser.php +++ b/src/WP_CLI/RecursiveDataStructureTraverser.php @@ -55,7 +55,7 @@ public function value() { return $this->data; } - public function set( $locator, $value ) { + public function update( $locator, $value ) { $this->traverse_to( (array) $locator )->set_value( $value ); } diff --git a/tests/RecursiveDataStructureTraverserTest.php b/tests/RecursiveDataStructureTraverserTest.php index c45a7d5db..90fff5a10 100644 --- a/tests/RecursiveDataStructureTraverserTest.php +++ b/tests/RecursiveDataStructureTraverserTest.php @@ -64,7 +64,7 @@ function it_can_set_a_nested_array_value() { $this->assertEquals( 'baz', $array['foo']['bar'] ); $traverser = new RecursiveDataStructureTraverser( $array ); - $traverser->set( array( 'foo', 'bar' ), 'new' ); + $traverser->update( array( 'foo', 'bar' ), 'new' ); $this->assertEquals( 'new', $array['foo']['bar'] ); } @@ -79,7 +79,7 @@ function it_can_set_a_nested_object_value() { $this->assertEquals( 'baz', $object->foo->bar ); $traverser = new RecursiveDataStructureTraverser( $object ); - $traverser->set( array( 'foo', 'bar' ), 'new' ); + $traverser->update( array( 'foo', 'bar' ), 'new' ); $this->assertEquals( 'new', $object->foo->bar ); } From 6a9229b50720c5c3ceefd97a9e6c7bef5f8c8902 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Sun, 16 Jul 2017 00:07:20 +0300 Subject: [PATCH 13/32] add support for patch delete --- features/post-meta.feature | 52 +++++++++++++++++++ src/WP_CLI/CommandWithMeta.php | 7 ++- tests/RecursiveDataStructureTraverserTest.php | 4 +- 3 files changed, 59 insertions(+), 4 deletions(-) diff --git a/features/post-meta.feature b/features/post-meta.feature index a76c9494c..dc3c98fa1 100644 --- a/features/post-meta.feature +++ b/features/post-meta.feature @@ -292,3 +292,55 @@ Feature: Manage post custom fields No data exists for not-a-key """ And the return code should be 1 + + @patch @patch-delete + Scenario: A key can be deleted from a nested value. + Given a WP install + And an input.json file: + """ + { + "foo": { + "bar": "baz", + "abe": "lincoln" + } + } + """ + And I run `wp post meta set 1 meta-key --format=json < input.json` + + When I run `wp post meta patch delete 1 meta-key foo bar` + Then STDOUT should be: + """ + Success: Updated custom field 'meta-key'. + """ + + When I run `wp post meta get 1 meta-key --format=json` + Then STDOUT should be JSON containing: + """ + { + "foo": { + "abe": "lincoln" + } + } + """ + + @patch @patch-fail @patch-delete @patch-delete-fail + Scenario: A key cannot be deleted from a nested value from a non-existent key. + Given a WP install + And an input.json file: + """ + { + "foo": { + "bar": "baz" + } + } + """ + And I run `wp post meta set 1 meta-key --format=json < input.json` + + When I try `wp post meta patch delete 1 meta-key foo not-a-key` + Then STDOUT should be empty + And STDERR should contain: + """ + No data exists for not-a-key + """ + And the return code should be 1 + diff --git a/src/WP_CLI/CommandWithMeta.php b/src/WP_CLI/CommandWithMeta.php index f5497719e..b96b15266 100644 --- a/src/WP_CLI/CommandWithMeta.php +++ b/src/WP_CLI/CommandWithMeta.php @@ -305,7 +305,7 @@ public function pluck( $args, $assoc_args ) { * options: * - add * - update - * - remove + * - delete * --- * * @@ -341,9 +341,12 @@ public function patch( $args, $assoc_args ) { stream_set_blocking( STDIN, 0 ); $stdin_value = trim( WP_CLI::get_value_from_arg_or_stdin( $args, -1 ) ); - if ( '' !== $stdin_value ) { + if ( 'delete' == $action ) { + $patch_value = null; + } elseif ( '' !== $stdin_value ) { $patch_value = WP_CLI::read_value( $stdin_value, $assoc_args ); } else { + // Take the patch value as the last positional argument. Mutates $key_path to be 1 element shorter! $patch_value = WP_CLI::read_value( array_pop( $key_path ), $assoc_args ); } diff --git a/tests/RecursiveDataStructureTraverserTest.php b/tests/RecursiveDataStructureTraverserTest.php index 90fff5a10..200904d9e 100644 --- a/tests/RecursiveDataStructureTraverserTest.php +++ b/tests/RecursiveDataStructureTraverserTest.php @@ -85,7 +85,7 @@ function it_can_set_a_nested_object_value() { } /** @test */ - function it_can_unset_a_nested_array_value() { + function it_can_delete_a_nested_array_value() { $array = array( 'foo' => array( 'bar' => 'baz', @@ -100,7 +100,7 @@ function it_can_unset_a_nested_array_value() { } /** @test */ - function it_can_unset_a_nested_object_value() { + function it_can_delete_a_nested_object_value() { $object = (object) array( 'foo' => (object) array( 'bar' => 'baz', From ce24ce0c5b1436d90c0fe3e6b6f85e12dc4480e5 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Sun, 16 Jul 2017 15:29:47 +0300 Subject: [PATCH 14/32] add insert method to traverser --- src/WP_CLI/NonExistentKeyException.php | 22 +++++++++++++ .../RecursiveDataStructureTraverser.php | 30 ++++++++++++++++-- tests/RecursiveDataStructureTraverserTest.php | 31 +++++++++++++++++++ 3 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 src/WP_CLI/NonExistentKeyException.php diff --git a/src/WP_CLI/NonExistentKeyException.php b/src/WP_CLI/NonExistentKeyException.php new file mode 100644 index 000000000..37a49a217 --- /dev/null +++ b/src/WP_CLI/NonExistentKeyException.php @@ -0,0 +1,22 @@ +traverser = $traverser; + } + + /** + * @return RecursiveDataStructureTraverser + */ + public function get_traverser() { + return $this->traverser; + } +} diff --git a/src/WP_CLI/RecursiveDataStructureTraverser.php b/src/WP_CLI/RecursiveDataStructureTraverser.php index ee98c1a56..e946cfbc1 100644 --- a/src/WP_CLI/RecursiveDataStructureTraverser.php +++ b/src/WP_CLI/RecursiveDataStructureTraverser.php @@ -67,6 +67,15 @@ public function delete( $locator ) { $this->traverse_to( $locator )->unset_on_parent(); } + public function insert( $locator, $value ) { + try { + $this->update( $locator, $value ); + } catch ( NonExistentKeyException $e ) { + $e->get_traverser()->create_key(); + $this->insert( $locator, $value ); + } + } + /** * Delete the key on the parent's data that references this data. */ @@ -99,9 +108,12 @@ public function delete_by_key( $key ) { public function traverse_to( $locator ) { if ( is_array( $locator ) && count( $locator ) ) { $current = array_shift( $locator ); + $this->set_key( $current ); if ( ! $this->exists( $current ) ) { - throw new \Exception( "No data exists for $current \n " . print_r( $this->data, true ) ); + $exception = new NonExistentKeyException( "No data exists for $current \n " . print_r( $this->data, true ) ); + $exception->set_traverser( $this ); + throw $exception; } foreach ( $this->data as $key => &$key_data ) { @@ -120,6 +132,20 @@ public function set_key( $key ) { $this->key = $key; } + /** + * @throws \Exception + * @internal param string $key + */ + protected function create_key() { + if ( is_array( $this->data ) ) { + $this->data[ $this->key ] = null; + } elseif ( is_object( $this->data ) ) { + $this->data->{$this->key} = null; + } else { + throw new \Exception( "Cannot create key '$this->key', invalid type." ); + } + } + /** * Check if the given key exists on the current data. * @@ -130,4 +156,4 @@ public function set_key( $key ) { public function exists( $key ) { return ( is_array( $this->data ) && array_key_exists( $key, $this->data ) ) || ( is_object( $this->data ) && property_exists( $this->data, $key ) ); } -} \ No newline at end of file +} diff --git a/tests/RecursiveDataStructureTraverserTest.php b/tests/RecursiveDataStructureTraverserTest.php index 200904d9e..f266fd63d 100644 --- a/tests/RecursiveDataStructureTraverserTest.php +++ b/tests/RecursiveDataStructureTraverserTest.php @@ -113,4 +113,35 @@ function it_can_delete_a_nested_object_value() { $this->assertObjectNotHasAttribute( 'bar', $object->foo ); } + + /** @test */ + function it_can_insert_a_key_into_a_nested_array() { + $array = array( + 'foo' => array( + 'bar' => 'baz', + ), + ); + + $traverser = new RecursiveDataStructureTraverser( $array ); + $traverser->insert( array( 'foo', 'new' ), 'new value' ); + + $this->assertArrayHasKey( 'new', $array['foo'] ); + $this->assertEquals( 'new value', $array['foo']['new'] ); + } + + /** @test */ + function it_throws_an_exception_when_attempting_to_create_a_key_on_an_invalid_type() { + $data = 'a string'; + $traverser = new RecursiveDataStructureTraverser( $data ); + + try { + $traverser->insert( array( 'key' ), 'value' ); + } catch ( \Exception $e ) { + $this->assertSame( 'a string', $data ); + return; + } + + $this->fail( 'Failed to assert that an exception was thrown when inserting a key into a string.' ); + } + } From f2c2e3f48b633b4152e34d3121dcae83a7390a10 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Sun, 16 Jul 2017 15:30:21 +0300 Subject: [PATCH 15/32] remove unnecessary casts --- src/WP_CLI/RecursiveDataStructureTraverser.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WP_CLI/RecursiveDataStructureTraverser.php b/src/WP_CLI/RecursiveDataStructureTraverser.php index e946cfbc1..c9f4d4e54 100644 --- a/src/WP_CLI/RecursiveDataStructureTraverser.php +++ b/src/WP_CLI/RecursiveDataStructureTraverser.php @@ -43,7 +43,7 @@ public function __construct( &$data, $parent = null ) { * @return static */ public function get( $locator ) { - return $this->traverse_to( (array) $locator )->value(); + return $this->traverse_to( $locator )->value(); } /** @@ -56,7 +56,7 @@ public function value() { } public function update( $locator, $value ) { - $this->traverse_to( (array) $locator )->set_value( $value ); + $this->traverse_to( $locator )->set_value( $value ); } public function set_value( $value ) { From 5ebce3398d099ead37a1b65303e1c1cd4aed4426 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Sun, 16 Jul 2017 15:30:53 +0300 Subject: [PATCH 16/32] add patch insert --- features/post-meta.feature | 37 ++++++++++++++++++++++++++++++++++ src/WP_CLI/CommandWithMeta.php | 2 +- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/features/post-meta.feature b/features/post-meta.feature index dc3c98fa1..be766dd2a 100644 --- a/features/post-meta.feature +++ b/features/post-meta.feature @@ -344,3 +344,40 @@ Feature: Manage post custom fields """ And the return code should be 1 + @patch @patch-insert + Scenario: A new key can be inserted into a nested value. + Given a WP install + And I run `wp post meta set 1 meta-key '{}' --format=json` + + When I run `wp post meta patch insert 1 meta-key foo bar` + Then STDOUT should be: + """ + Success: Updated custom field 'meta-key'. + """ + + When I run `wp post meta get 1 meta-key --format=json` + Then STDOUT should be JSON containing: + """ + { + "foo": "bar" + } + """ + + @patch @patch-fail @patch-insert @patch-insert-fail + Scenario: A new key cannot be inserted into a non-nested value. + Given a WP install + And I run `wp post meta set 1 meta-key 'a simple value'` + + When I try `wp post meta patch insert 1 meta-key foo bar` + Then STDOUT should be empty + And STDERR should contain: + """ + Cannot create key 'foo' + """ + And the return code should be 1 + + When I run `wp post meta get 1 meta-key` + Then STDOUT should be: + """ + a simple value + """ \ No newline at end of file diff --git a/src/WP_CLI/CommandWithMeta.php b/src/WP_CLI/CommandWithMeta.php index b96b15266..e851b8d83 100644 --- a/src/WP_CLI/CommandWithMeta.php +++ b/src/WP_CLI/CommandWithMeta.php @@ -303,7 +303,7 @@ public function pluck( $args, $assoc_args ) { * : Patch action to perform. * --- * options: - * - add + * - insert * - update * - delete * --- From d2939902edc9f6afcaf07d30271aa80b383f6a79 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Sun, 16 Jul 2017 18:23:08 +0300 Subject: [PATCH 17/32] Revert "remove unnecessary casts" This reverts commit f2c2e3f48b633b4152e34d3121dcae83a7390a10. --- src/WP_CLI/RecursiveDataStructureTraverser.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WP_CLI/RecursiveDataStructureTraverser.php b/src/WP_CLI/RecursiveDataStructureTraverser.php index c9f4d4e54..e946cfbc1 100644 --- a/src/WP_CLI/RecursiveDataStructureTraverser.php +++ b/src/WP_CLI/RecursiveDataStructureTraverser.php @@ -43,7 +43,7 @@ public function __construct( &$data, $parent = null ) { * @return static */ public function get( $locator ) { - return $this->traverse_to( $locator )->value(); + return $this->traverse_to( (array) $locator )->value(); } /** @@ -56,7 +56,7 @@ public function value() { } public function update( $locator, $value ) { - $this->traverse_to( $locator )->set_value( $value ); + $this->traverse_to( (array) $locator )->set_value( $value ); } public function set_value( $value ) { From 073970441ba847f35227d6a9cd41a15d5b270b1e Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Sun, 16 Jul 2017 18:25:47 +0300 Subject: [PATCH 18/32] add cast in delete for consistency --- src/WP_CLI/RecursiveDataStructureTraverser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WP_CLI/RecursiveDataStructureTraverser.php b/src/WP_CLI/RecursiveDataStructureTraverser.php index e946cfbc1..ac1195dbf 100644 --- a/src/WP_CLI/RecursiveDataStructureTraverser.php +++ b/src/WP_CLI/RecursiveDataStructureTraverser.php @@ -64,7 +64,7 @@ public function set_value( $value ) { } public function delete( $locator ) { - $this->traverse_to( $locator )->unset_on_parent(); + $this->traverse_to( (array) $locator )->unset_on_parent(); } public function insert( $locator, $value ) { From dc488c7873ceaabe83fff6ec5af0a11044df4c39 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Sun, 16 Jul 2017 19:04:08 +0300 Subject: [PATCH 19/32] add initial support for pluck/patch with int keys --- features/post-meta.feature | 28 ++++++++++++++++++++++++++++ src/WP_CLI/CommandWithMeta.php | 14 ++++++++++++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/features/post-meta.feature b/features/post-meta.feature index be766dd2a..b41e352cd 100644 --- a/features/post-meta.feature +++ b/features/post-meta.feature @@ -211,6 +211,17 @@ Feature: Manage post custom fields Then STDOUT should be empty And the return code should be 1 + @pluck @pluck-numeric + Scenario: A nested value can be retrieved from an integer key. + Given a WP install + And I run `wp post meta set 1 meta-key '[ "foo", "bar" ]' --format=json` + + When I run `wp post meta pluck 1 meta-key 0` + Then STDOUT should be: + """ + foo + """ + @patch @patch-update @patch-arg Scenario: Nested values can be changed. Given a WP install @@ -380,4 +391,21 @@ Feature: Manage post custom fields Then STDOUT should be: """ a simple value + """ + + @patch @patch-numeric + Scenario: A nested value can be updated using an integer key. + Given a WP install + And I run `wp post meta set 1 meta-key '[ "foo", "bar" ]' --format=json` + + When I run `wp post meta patch update 1 meta-key 0 new` + Then STDOUT should be: + """ + Success: Updated custom field 'meta-key'. + """ + + When I run `wp post meta get 1 meta-key --format=json` + Then STDOUT should be JSON containing: + """ + [ "new", "bar" ] """ \ No newline at end of file diff --git a/src/WP_CLI/CommandWithMeta.php b/src/WP_CLI/CommandWithMeta.php index e851b8d83..014781903 100644 --- a/src/WP_CLI/CommandWithMeta.php +++ b/src/WP_CLI/CommandWithMeta.php @@ -279,7 +279,12 @@ public function update( $args, $assoc_args ) { public function pluck( $args, $assoc_args ) { list( $object_id, $meta_key ) = $args; $object_id = $this->check_object_id( $object_id ); - $key_path = array_slice( $args, 2 ); + $key_path = array_map( function( $key ) { + if ( is_numeric( $key ) && ( $key === (string) intval( $key ) ) ) { + return (int) $key; + } + return $key; + }, array_slice( $args, 2 ) ); $value = get_metadata( $this->meta_type, $object_id, $meta_key, true ); @@ -332,7 +337,12 @@ public function pluck( $args, $assoc_args ) { public function patch( $args, $assoc_args ) { list( $action, $object_id, $meta_key ) = $args; $object_id = $this->check_object_id( $object_id ); - $key_path = array_slice( $args, 3 ); + $key_path = array_map( function( $key ) { + if ( is_numeric( $key ) && ( $key === (string) intval( $key ) ) ) { + return (int) $key; + } + return $key; + }, array_slice( $args, 3 ) ); /** * We need to check STDIN first as we have no way of determining the index of the value. From a7c65453a9687d34b071a51d9b8217546492e043 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Mon, 31 Jul 2017 07:49:47 +0200 Subject: [PATCH 20/32] fix schema URL in phpunit config --- phpunit.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpunit.xml b/phpunit.xml index 1b43955e8..16cff729f 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,6 +1,6 @@ Date: Mon, 31 Jul 2017 08:06:11 +0200 Subject: [PATCH 21/32] organize traverser classes within Entity namespace --- src/WP_CLI/CommandWithMeta.php | 1 + src/WP_CLI/{ => Entity}/NonExistentKeyException.php | 8 ++++---- .../{ => Entity}/RecursiveDataStructureTraverser.php | 2 +- tests/RecursiveDataStructureTraverserTest.php | 4 +++- 4 files changed, 9 insertions(+), 6 deletions(-) rename src/WP_CLI/{ => Entity}/NonExistentKeyException.php (54%) rename src/WP_CLI/{ => Entity}/RecursiveDataStructureTraverser.php (99%) diff --git a/src/WP_CLI/CommandWithMeta.php b/src/WP_CLI/CommandWithMeta.php index 014781903..e40ffaa2a 100644 --- a/src/WP_CLI/CommandWithMeta.php +++ b/src/WP_CLI/CommandWithMeta.php @@ -3,6 +3,7 @@ namespace WP_CLI; use WP_CLI; +use WP_CLI\Entity\RecursiveDataStructureTraverser; /** * Base class for WP-CLI commands that deal with metadata diff --git a/src/WP_CLI/NonExistentKeyException.php b/src/WP_CLI/Entity/NonExistentKeyException.php similarity index 54% rename from src/WP_CLI/NonExistentKeyException.php rename to src/WP_CLI/Entity/NonExistentKeyException.php index 37a49a217..7b4b80441 100644 --- a/src/WP_CLI/NonExistentKeyException.php +++ b/src/WP_CLI/Entity/NonExistentKeyException.php @@ -1,20 +1,20 @@ traverser = $traverser; } /** - * @return RecursiveDataStructureTraverser + * @return \WP_CLI\Entity\RecursiveDataStructureTraverser */ public function get_traverser() { return $this->traverser; diff --git a/src/WP_CLI/RecursiveDataStructureTraverser.php b/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php similarity index 99% rename from src/WP_CLI/RecursiveDataStructureTraverser.php rename to src/WP_CLI/Entity/RecursiveDataStructureTraverser.php index ac1195dbf..b2e5de080 100644 --- a/src/WP_CLI/RecursiveDataStructureTraverser.php +++ b/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php @@ -1,6 +1,6 @@ Date: Mon, 31 Jul 2017 08:06:49 +0200 Subject: [PATCH 22/32] change base exception for NonExistentKeyException --- src/WP_CLI/Entity/NonExistentKeyException.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WP_CLI/Entity/NonExistentKeyException.php b/src/WP_CLI/Entity/NonExistentKeyException.php index 7b4b80441..be007d195 100644 --- a/src/WP_CLI/Entity/NonExistentKeyException.php +++ b/src/WP_CLI/Entity/NonExistentKeyException.php @@ -2,7 +2,7 @@ namespace WP_CLI\Entity; -class NonExistentKeyException extends \Exception { +class NonExistentKeyException extends \OutOfBoundsException { /* @var \WP_CLI\Entity\RecursiveDataStructureTraverser */ protected $traverser; From 96da0243968453995c8d48476eccde234c1c2203 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Mon, 31 Jul 2017 08:28:19 +0200 Subject: [PATCH 23/32] =?UTF-8?q?update=20exception=20thrown=20in=20traver?= =?UTF-8?q?ser=E2=80=99s=20create=5Fkey?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/WP_CLI/Entity/RecursiveDataStructureTraverser.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php b/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php index b2e5de080..94f4d3c18 100644 --- a/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php +++ b/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php @@ -133,8 +133,7 @@ public function set_key( $key ) { } /** - * @throws \Exception - * @internal param string $key + * @throws \UnexpectedValueException */ protected function create_key() { if ( is_array( $this->data ) ) { @@ -142,7 +141,7 @@ protected function create_key() { } elseif ( is_object( $this->data ) ) { $this->data->{$this->key} = null; } else { - throw new \Exception( "Cannot create key '$this->key', invalid type." ); + throw new \UnexpectedValueException( sprintf( 'Cannot create key "%s" on data type %s', $this->key, gettype( $this->data ) ) ); } } From 868f0c333a08571b50c023e83d9e04b3cc52b3a7 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Mon, 31 Jul 2017 09:09:14 +0200 Subject: [PATCH 24/32] docblocks and var name changes --- .../RecursiveDataStructureTraverser.php | 68 ++++++++++++++----- 1 file changed, 50 insertions(+), 18 deletions(-) diff --git a/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php b/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php index 94f4d3c18..a642832fa 100644 --- a/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php +++ b/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php @@ -27,7 +27,7 @@ class RecursiveDataStructureTraverser { /** * RecursiveDataStructureTraverser constructor. * - * @param $data + * @param mixed $data The data to read/manipulate by reference. * @param static $parent */ public function __construct( &$data, $parent = null ) { @@ -36,14 +36,14 @@ public function __construct( &$data, $parent = null ) { } /** - * @param $locator + * Get the nested value at the given key path. * - * @throws \Exception + * @param string|int|array $key_path * * @return static */ - public function get( $locator ) { - return $this->traverse_to( (array) $locator )->value(); + public function get( $key_path ) { + return $this->traverse_to( (array) $key_path )->value(); } /** @@ -55,24 +55,49 @@ public function value() { return $this->data; } - public function update( $locator, $value ) { - $this->traverse_to( (array) $locator )->set_value( $value ); + /** + * Update a nested value at the given key path. + * + * @param string|int|array $key_path + * @param mixed $value + */ + public function update( $key_path, $value ) { + $this->traverse_to( (array) $key_path )->set_value( $value ); } + /** + * Update the current data with the given value. + * + * This will mutate the variable which was passed into the constructor + * as the data is set and traversed by reference. + * + * @param mixed $value + */ public function set_value( $value ) { $this->data = $value; } - public function delete( $locator ) { - $this->traverse_to( (array) $locator )->unset_on_parent(); + /** + * Unset the value at the given key path. + * + * @param $key_path + */ + public function delete( $key_path ) { + $this->traverse_to( (array) $key_path )->unset_on_parent(); } - public function insert( $locator, $value ) { + /** + * Define a nested value while creating keys if they do not exist. + * + * @param array $key_path + * @param mixed $value + */ + public function insert( $key_path, $value ) { try { - $this->update( $locator, $value ); + $this->update( $key_path, $value ); } catch ( NonExistentKeyException $e ) { $e->get_traverser()->create_key(); - $this->insert( $locator, $value ); + $this->insert( $key_path, $value ); } } @@ -99,15 +124,15 @@ public function delete_by_key( $key ) { /** * Get an instance of the traverser for the given hierarchical key. * - * @param $locator + * @param array $key_path Hierarchical key path within the current data to traverse to. * - * @throws \Exception + * @throws NonExistentKeyException * * @return static */ - public function traverse_to( $locator ) { - if ( is_array( $locator ) && count( $locator ) ) { - $current = array_shift( $locator ); + public function traverse_to( $key_path ) { + if ( is_array( $key_path ) && count( $key_path ) ) { + $current = array_shift( $key_path ); $this->set_key( $current ); if ( ! $this->exists( $current ) ) { @@ -120,7 +145,7 @@ public function traverse_to( $locator ) { if ( $key === $current ) { $traverser = new static( $key_data, $this ); $traverser->set_key( $key ); - return $traverser->traverse_to( $locator ); + return $traverser->traverse_to( $key_path ); } } } @@ -128,11 +153,18 @@ public function traverse_to( $locator ) { return $this; } + /** + * Define the key for the current data. + * + * @param string $key + */ public function set_key( $key ) { $this->key = $key; } /** + * Create the key on the current data. + * * @throws \UnexpectedValueException */ protected function create_key() { From 057a9307b662f9113906bccce39276cd2dc20bf5 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Mon, 31 Jul 2017 09:22:15 +0200 Subject: [PATCH 25/32] remove vestigial delimiter property --- src/WP_CLI/Entity/RecursiveDataStructureTraverser.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php b/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php index a642832fa..d8403d01c 100644 --- a/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php +++ b/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php @@ -9,11 +9,6 @@ class RecursiveDataStructureTraverser { */ protected $data; - /** - * @var string The character/sequence used to delineate hierarchy in a single key. - */ - protected $delimiter; - /** * @var null|string The key the data belongs to in the parent's data. */ From de2d604681062e71a5d4fd726ad6332594122708 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Mon, 31 Jul 2017 09:25:10 +0200 Subject: [PATCH 26/32] move key to constructor parameter --- .../Entity/RecursiveDataStructureTraverser.php | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php b/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php index d8403d01c..8a647d4a6 100644 --- a/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php +++ b/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php @@ -23,10 +23,12 @@ class RecursiveDataStructureTraverser { * RecursiveDataStructureTraverser constructor. * * @param mixed $data The data to read/manipulate by reference. + * @param string|int $key The key/property the data belongs to. * @param static $parent */ - public function __construct( &$data, $parent = null ) { + public function __construct( &$data, $key = null, $parent = null ) { $this->data =& $data; + $this->key = $key; $this->parent = $parent; } @@ -128,7 +130,7 @@ public function delete_by_key( $key ) { public function traverse_to( $key_path ) { if ( is_array( $key_path ) && count( $key_path ) ) { $current = array_shift( $key_path ); - $this->set_key( $current ); + $this->key = $current; if ( ! $this->exists( $current ) ) { $exception = new NonExistentKeyException( "No data exists for $current \n " . print_r( $this->data, true ) ); @@ -138,8 +140,7 @@ public function traverse_to( $key_path ) { foreach ( $this->data as $key => &$key_data ) { if ( $key === $current ) { - $traverser = new static( $key_data, $this ); - $traverser->set_key( $key ); + $traverser = new static( $key_data, $key, $this ); return $traverser->traverse_to( $key_path ); } } @@ -148,15 +149,6 @@ public function traverse_to( $key_path ) { return $this; } - /** - * Define the key for the current data. - * - * @param string $key - */ - public function set_key( $key ) { - $this->key = $key; - } - /** * Create the key on the current data. * From 2ab55f45f8341c562dc3728951d0dbcf5095bb92 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Mon, 31 Jul 2017 09:33:00 +0200 Subject: [PATCH 27/32] improve exception message for a missing key --- features/post-meta.feature | 4 ++-- src/WP_CLI/Entity/RecursiveDataStructureTraverser.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/features/post-meta.feature b/features/post-meta.feature index b41e352cd..d4775808e 100644 --- a/features/post-meta.feature +++ b/features/post-meta.feature @@ -300,7 +300,7 @@ Feature: Manage post custom fields Then STDOUT should be empty And STDERR should contain: """ - No data exists for not-a-key + No data exists for key "not-a-key" """ And the return code should be 1 @@ -351,7 +351,7 @@ Feature: Manage post custom fields Then STDOUT should be empty And STDERR should contain: """ - No data exists for not-a-key + No data exists for key "not-a-key" """ And the return code should be 1 diff --git a/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php b/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php index 8a647d4a6..c78aac4fb 100644 --- a/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php +++ b/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php @@ -133,7 +133,7 @@ public function traverse_to( $key_path ) { $this->key = $current; if ( ! $this->exists( $current ) ) { - $exception = new NonExistentKeyException( "No data exists for $current \n " . print_r( $this->data, true ) ); + $exception = new NonExistentKeyException( "No data exists for key \"$current\"" ); $exception->set_traverser( $this ); throw $exception; } From 46a9e5644903fbb8fa47144f36f9558360c19971 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Mon, 31 Jul 2017 09:34:08 +0200 Subject: [PATCH 28/32] simplify traverse_to method --- .../RecursiveDataStructureTraverser.php | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php b/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php index c78aac4fb..ac64e2e37 100644 --- a/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php +++ b/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php @@ -127,26 +127,25 @@ public function delete_by_key( $key ) { * * @return static */ - public function traverse_to( $key_path ) { - if ( is_array( $key_path ) && count( $key_path ) ) { - $current = array_shift( $key_path ); - $this->key = $current; - - if ( ! $this->exists( $current ) ) { - $exception = new NonExistentKeyException( "No data exists for key \"$current\"" ); - $exception->set_traverser( $this ); - throw $exception; - } + public function traverse_to( array $key_path ) { + $current = array_shift( $key_path ); - foreach ( $this->data as $key => &$key_data ) { - if ( $key === $current ) { - $traverser = new static( $key_data, $key, $this ); - return $traverser->traverse_to( $key_path ); - } - } + if ( null === $current ) { + return $this; + } + + if ( ! $this->exists( $current ) ) { + $exception = new NonExistentKeyException( "No data exists for key \"$current\"" ); + $exception->set_traverser( new static( $this->data, $current, $this->parent ) ); + throw $exception; } - return $this; + foreach ( $this->data as $key => &$key_data ) { + if ( $key === $current ) { + $traverser = new static( $key_data, $key, $this ); + return $traverser->traverse_to( $key_path ); + } + } } /** From 4b89ef96f187d9bb4601593aec24e2a4ee55c4ef Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Mon, 31 Jul 2017 09:42:24 +0200 Subject: [PATCH 29/32] update quote used in behat test after change --- features/post-meta.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/post-meta.feature b/features/post-meta.feature index d4775808e..45398c3a5 100644 --- a/features/post-meta.feature +++ b/features/post-meta.feature @@ -383,7 +383,7 @@ Feature: Manage post custom fields Then STDOUT should be empty And STDERR should contain: """ - Cannot create key 'foo' + Cannot create key "foo" """ And the return code should be 1 From 5691612d364057826156400b41fe8632e26d0187 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Mon, 31 Jul 2017 09:42:47 +0200 Subject: [PATCH 30/32] break long line for readability --- src/WP_CLI/Entity/RecursiveDataStructureTraverser.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php b/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php index ac64e2e37..a7b7c93f6 100644 --- a/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php +++ b/src/WP_CLI/Entity/RecursiveDataStructureTraverser.php @@ -171,6 +171,7 @@ protected function create_key() { * @return bool */ public function exists( $key ) { - return ( is_array( $this->data ) && array_key_exists( $key, $this->data ) ) || ( is_object( $this->data ) && property_exists( $this->data, $key ) ); + return ( is_array( $this->data ) && array_key_exists( $key, $this->data ) ) || + ( is_object( $this->data ) && property_exists( $this->data, $key ) ); } } From 10f232e1086a51bbf69c09a5d36dc47883eacf29 Mon Sep 17 00:00:00 2001 From: Evan Mattson Date: Mon, 31 Jul 2017 10:03:54 +0200 Subject: [PATCH 31/32] update patch with real stdin detection! --- src/WP_CLI/CommandWithMeta.php | 13 ++++--------- src/WP_CLI/Entity/Utils.php | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 src/WP_CLI/Entity/Utils.php diff --git a/src/WP_CLI/CommandWithMeta.php b/src/WP_CLI/CommandWithMeta.php index e40ffaa2a..2ab818a8f 100644 --- a/src/WP_CLI/CommandWithMeta.php +++ b/src/WP_CLI/CommandWithMeta.php @@ -4,6 +4,7 @@ use WP_CLI; use WP_CLI\Entity\RecursiveDataStructureTraverser; +use WP_CLI\Entity\Utils; /** * Base class for WP-CLI commands that deal with metadata @@ -345,17 +346,11 @@ public function patch( $args, $assoc_args ) { return $key; }, array_slice( $args, 3 ) ); - /** - * We need to check STDIN first as we have no way of determining the index of the value. - * We set the read from STDIN to non-blocking, or it will be stuck in an infinite loop if not passed. - */ - stream_set_blocking( STDIN, 0 ); - $stdin_value = trim( WP_CLI::get_value_from_arg_or_stdin( $args, -1 ) ); - if ( 'delete' == $action ) { $patch_value = null; - } elseif ( '' !== $stdin_value ) { - $patch_value = WP_CLI::read_value( $stdin_value, $assoc_args ); + } elseif ( Utils::has_stdin() ) { + $stdin_value = WP_CLI::get_value_from_arg_or_stdin( $args, -1 ); + $patch_value = WP_CLI::read_value( trim( $stdin_value ), $assoc_args ); } else { // Take the patch value as the last positional argument. Mutates $key_path to be 1 element shorter! $patch_value = WP_CLI::read_value( array_pop( $key_path ), $assoc_args ); diff --git a/src/WP_CLI/Entity/Utils.php b/src/WP_CLI/Entity/Utils.php new file mode 100644 index 000000000..7c2d92d6e --- /dev/null +++ b/src/WP_CLI/Entity/Utils.php @@ -0,0 +1,22 @@ + Date: Tue, 1 Aug 2017 22:01:15 +0200 Subject: [PATCH 32/32] remove entity utils import breaks relative namespacing to WP_CLI\Utils functions --- src/WP_CLI/CommandWithMeta.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/WP_CLI/CommandWithMeta.php b/src/WP_CLI/CommandWithMeta.php index 2ab818a8f..d1092c884 100644 --- a/src/WP_CLI/CommandWithMeta.php +++ b/src/WP_CLI/CommandWithMeta.php @@ -4,7 +4,6 @@ use WP_CLI; use WP_CLI\Entity\RecursiveDataStructureTraverser; -use WP_CLI\Entity\Utils; /** * Base class for WP-CLI commands that deal with metadata @@ -348,7 +347,7 @@ public function patch( $args, $assoc_args ) { if ( 'delete' == $action ) { $patch_value = null; - } elseif ( Utils::has_stdin() ) { + } elseif ( Entity\Utils::has_stdin() ) { $stdin_value = WP_CLI::get_value_from_arg_or_stdin( $args, -1 ); $patch_value = WP_CLI::read_value( trim( $stdin_value ), $assoc_args ); } else {