Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions lib/class-importer.php
Original file line number Diff line number Diff line change
Expand Up @@ -433,9 +433,12 @@ protected function import_class( array $data, $import_ignored = false ) {
// Set class-specific meta
update_post_meta( $class_id, '_wp-parser_final', (string) $data['final'] );
update_post_meta( $class_id, '_wp-parser_abstract', (string) $data['abstract'] );
update_post_meta( $class_id, '_wp-parser_extends', $data['extends'] );
update_post_meta( $class_id, '_wp-parser_implements', $data['implements'] );
update_post_meta( $class_id, '_wp-parser_properties', $data['properties'] );
// Metadata APIs unslash their input. map_deep() reaches every nested
// string, preserving the namespace separators in class relatives and
// property types.
update_post_meta( $class_id, '_wp-parser_extends', map_deep( $data['extends'], 'wp_slash' ) );
update_post_meta( $class_id, '_wp-parser_implements', map_deep( $data['implements'], 'wp_slash' ) );
update_post_meta( $class_id, '_wp-parser_properties', map_deep( $data['properties'], 'wp_slash' ) );

// Now add the methods
foreach ( $data['methods'] as $method ) {
Expand Down Expand Up @@ -743,13 +746,16 @@ public function import_item( array $data, $parent_post_id = 0, $import_ignored =
$data['doc']['tags']['deprecated'] = $this->file_meta['deprecated'];
}

// Metadata APIs unslash their input. map_deep() reaches every nested
// string, preserving the namespace separators in argument and alias
// types.
if ( $post_data['post_type'] !== $this->post_type_class ) {
$anything_updated[] = update_post_meta( $post_id, '_wp-parser_args', $data['arguments'] );
$anything_updated[] = update_post_meta( $post_id, '_wp-parser_args', map_deep( $data['arguments'], 'wp_slash' ) );
}

// If the post type is using namespace aliases, record them.
if ( ! empty( $data['aliases'] ) ) {
$anything_updated[] = update_post_meta( $post_id, '_wp_parser_aliases', (array) $data['aliases'] );
$anything_updated[] = update_post_meta( $post_id, '_wp_parser_aliases', map_deep( (array) $data['aliases'], 'wp_slash' ) );
}

// Recored the namespace if there is one.
Expand All @@ -759,7 +765,11 @@ public function import_item( array $data, $parent_post_id = 0, $import_ignored =

$anything_updated[] = update_post_meta( $post_id, '_wp-parser_line_num', (string) $data['line'] );
$anything_updated[] = update_post_meta( $post_id, '_wp-parser_end_line_num', (string) $data['end_line'] );
$anything_updated[] = update_post_meta( $post_id, '_wp-parser_tags', $data['doc']['tags'] );

// Metadata APIs unslash their input. map_deep() reaches every nested
// string, preserving the namespace separators in tag types and
// references such as `@param \Foo\Bar` and `@see \Foo\Bar`.
$anything_updated[] = update_post_meta( $post_id, '_wp-parser_tags', map_deep( $data['doc']['tags'], 'wp_slash' ) );

// Metadata APIs unslash their input. map_deep() reaches retained JSON
// objects as well as arrays, preserving backslashes in PHP and Blueprint
Expand Down
161 changes: 161 additions & 0 deletions tests/phpunit/tests/import/file.php
Original file line number Diff line number Diff line change
Expand Up @@ -213,4 +213,165 @@ public function test_function_snippet_metadata_preserves_backslashes() {
$this->assertEquals( $snippets, get_post_meta( $post->ID, '_wp-parser_code_snippets', true ) );
$this->assertEquals( $setup_blueprints, get_post_meta( $post->ID, '_wp-parser_setup_blueprints', true ) );
}

/**
* Test that WordPress metadata slashing does not alter DocBlock tags.
*/
public function test_function_tag_metadata_preserves_backslashes() {

$posts = get_posts(
array( 'post_type' => $this->importer->post_type_function )
);
$post = $posts[0];

$function_data = $this->export_data['functions'][0];
$tags = array(
array(
'name' => 'see',
'content' => '',
'refers' => '\Docs\Example::method()',
),
array(
'name' => 'param',
'content' => 'A namespaced parameter.',
'types' => array( '\Foo', '\Foo\Bar', 'Vendor\Foo' ),
'variable' => '$var',
),
);

$function_data['doc']['tags'] = $tags;

$this->importer->import_function( $function_data );

$this->assertEquals( $tags, get_post_meta( $post->ID, '_wp-parser_tags', true ) );
}

/**
* Test that WordPress metadata slashing does not alter argument metadata.
*/
public function test_function_argument_metadata_preserves_backslashes() {

$posts = get_posts(
array( 'post_type' => $this->importer->post_type_function )
);
$post = $posts[0];

$function_data = $this->export_data['functions'][0];
$arguments = array(
array(
'name' => '$leading',
'default' => null,
'type' => '\Foo',
),
array(
'name' => '$qualified',
'default' => null,
'type' => '\Foo\Bar',
),
array(
'name' => '$relative',
'default' => '\Vendor\Foo::DEFAULT_VALUE',
'type' => 'Vendor\Foo',
),
);

$function_data['arguments'] = $arguments;

$this->importer->import_function( $function_data );

$this->assertEquals( $arguments, get_post_meta( $post->ID, '_wp-parser_args', true ) );
}

/**
* Test that WordPress metadata slashing does not alter namespace aliases.
*/
public function test_function_alias_metadata_preserves_backslashes() {

$posts = get_posts(
array( 'post_type' => $this->importer->post_type_function )
);
$post = $posts[0];

$function_data = $this->export_data['functions'][0];
$aliases = array(
'Leading' => '\Foo',
'Qualified' => '\Foo\Bar',
'Relative' => 'Vendor\Foo',
);

$function_data['aliases'] = $aliases;

$this->importer->import_function( $function_data );

$this->assertEquals( $aliases, get_post_meta( $post->ID, '_wp_parser_aliases', true ) );
}

/**
* Test that WordPress metadata slashing does not alter class metadata.
*/
public function test_class_metadata_preserves_backslashes() {

$properties = array(
array(
'name' => '$example',
'line' => 12,
'end_line' => 12,
'default' => '\Vendor\Foo::DEFAULT_VALUE',
'static' => false,
'visibility' => 'public',
'doc' => array(
'description' => '',
'long_description' => '',
'tags' => array(
array(
'name' => 'var',
'content' => 'A namespaced property.',
'types' => array( '\Foo', '\Foo\Bar', 'Vendor\Foo' ),
'variable' => '',
),
),
),
),
);

$class_data = array(
'name' => 'Slashing_Example',
'namespace' => 'Vendor\Docs',
'line' => 10,
'end_line' => 14,
'final' => false,
'abstract' => false,
'extends' => '\Foo\Bar',
'implements' => array( '\Foo', 'Vendor\Foo' ),
'properties' => $properties,
'methods' => array(),
'doc' => array(
'description' => 'A class with namespaced relatives.',
'long_description' => '',
'tags' => array(),
),
);

$file_data = $this->export_data;
$file_data['functions'] = array();
$file_data['classes'] = array( $class_data );
$file_data['hooks'] = array();

$this->importer->import_file( $file_data, true );

$posts = get_posts(
array( 'post_type' => $this->importer->post_type_class )
);

$this->assertCount( 1, $posts );

$post = $posts[0];

$this->assertEquals( '\Foo\Bar', get_post_meta( $post->ID, '_wp-parser_extends', true ) );
$this->assertEquals( array( '\Foo', 'Vendor\Foo' ), get_post_meta( $post->ID, '_wp-parser_implements', true ) );
$this->assertEquals( $properties, get_post_meta( $post->ID, '_wp-parser_properties', true ) );

// The namespace is already compensated for; it must not be slashed twice.
$this->assertEquals( 'Vendor\Docs', get_post_meta( $post->ID, '_wp_parser_namespace', true ) );
}
}