Repository navigation
Conversation
phpDocumentor's reflection-docblock 2.x splits a tag's content on the first whitespace and its type on every `|`, with no bracket awareness. `@param array<string, mixed> $l10n Desc` exports the type `array<string,`, no variable, and the description `mixed> $l10n Desc`; `list<string|MessagePart|array>` becomes three types. Generic arguments are never resolved, and PHPDoc keywords such as `list`, `?string` or `class-string<...>` are prefixed with the namespace as if they were class names. Read the type at the start of `@param`, `@var`, `@property*`, `@return` and `@throws` content with phpstan/phpdoc-parser's TypeParser, then the variable and description after it. phpDocumentor still splits the DocBlock into tags and descriptions; only how a tag's type, variable and description are derived changes. The exported `types` array keeps its contract: one string per top-level union member, each printed as written, with class names resolved against the DocBlock's namespace and `use` aliases as phpDocumentor resolved them, now including names inside generics, shapes, callables and class constant patterns. Keywords, PHPStan pseudo-types, shape keys and the DocBlock's own `@template` names are not prefixed. A by-reference `&$name` now yields the variable name. When PHPStan's parser rejects a tag, or the type does not end at whitespace, the tag is exported exactly as before.
WordPress documents array keys with its own hash notation: a tag
description of the form `{ intro @type Type $key Desc. ... }`, nested
when a `@type` line ends in `{`. The parser exports it only as flat text
in the tag's `content`, which the theme re-parses at render time by
splitting each `@type` line on spaces, so a generic such as
`array<string, mixed>` breaks there too.
When a typed tag's description is a hash, also export a `hash` key:
`content` holds the text before the first `@type` line, and `items` one
entry per `@type` line with `types` (read like a tag's, falling back to
phpDocumentor's reading when PHPStan's parser rejects it), `variable`
(as written: `$label`, `$0`, `...$0`, or empty), `content`, and a nested
`hash` when the line opens one. A `@return` that names the array before
the brace (`@return array $args {`) is read the same way. A hash whose
braces do not balance gets no `hash` key.
The tag's `content` string is unchanged, so the theme keeps working
until it reads `hash`.
Generic types now reach the export intact, and typed tags may carry a `hash` key. The importer stores tags in `_wp-parser_tags` with update_post_meta(), which applies no KSES, so `<...>` in types and the nested `hash` structure are stored as exported and need no importer change. `make_args_safe()`, which does run wp_filter_kses(), is attached to the `wp_parser_get_arguments` filter in the plugin's template functions, not to import. Namespace separators are a separate case: update_post_meta() unslashes them, which #287 addresses.
Contributor
Corpus diff1430 hunks, corpus.diff (33 of 1430 hunks)--- base
+++ head
@@ -4596,7 +4596,33 @@
"types": [
"array"
],
- "variable": "$request"
+ "variable": "$request",
+ "hash": {
+ "content": "The array of request data. All arguments are optional and may be empty.",
+ "items": [
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$app_name",
+ "content": "The suggested name of the application."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$success_url",
+ "content": "The URL the user will be redirected to after approving the application."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$reject_url",
+ "content": "The URL the user will be redirected to after rejecting the application."
+ }
+ ]
+ }
},
{
"name": "param",
@@ -13307,7 +13333,62 @@
"types": [
"array"
],
- "variable": "$dropdown_args"
+ "variable": "$dropdown_args",
+ "hash": {
+ "content": "An array of taxonomy parent drop-down arguments.",
+ "items": [
+ {
+ "types": [
+ "int",
+ "bool"
+ ],
+ "variable": "$hide_empty",
+ "content": "Whether to hide terms not attached to any posts. Default 0."
+ },
+ {
+ "types": [
+ "bool"
+ ],
+ "variable": "$hide_if_empty",
+ "content": "Whether to hide the drop-down if no terms exist. Default false."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$taxonomy",
+ "content": "The taxonomy slug."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$name",
+ "content": "Value of the name attribute to use for the drop-down select element.<br>Default 'parent'."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$orderby",
+ "content": "The field to order by. Default 'name'."
+ },
+ {
+ "types": [
+ "bool"
+ ],
+ "variable": "$hierarchical",
+ "content": "Whether the taxonomy is hierarchical. Default true."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$show_option_none",
+ "content": "Label to display if there are no terms. Default 'None'."
+ }
+ ]
+ }
},
{
"name": "param",
@@ -28892,7 +28973,38 @@
"types": [
"array"
],
- "variable": "$args"
+ "variable": "$args",
+ "hash": {
+ "content": "An array of callable exporters of personal data. Default empty array.",
+ "items": [
+ {
+ "types": [
+ "array"
+ ],
+ "variable": "...$0",
+ "content": "",
+ "hash": {
+ "content": "Array of personal data exporters.",
+ "items": [
+ {
+ "types": [
+ "callable"
+ ],
+ "variable": "$callback",
+ "content": "Callable exporter function that accepts an email address and a page number and returns an array of name => value pairs of personal data."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$exporter_friendly_name",
+ "content": "Translated user facing friendly name for the exporter."
+ }
+ ]
+ }
+ }
+ ]
+ }
}
]
}
@@ -29404,7 +29516,38 @@
"types": [
"array"
],
- "variable": "$args"
+ "variable": "$args",
+ "hash": {
+ "content": "An array of callable erasers of personal data. Default empty array.",
+ "items": [
+ {
+ "types": [
+ "array"
+ ],
+ "variable": "...$0",
+ "content": "",
+ "hash": {
+ "content": "Array of personal data exporters.",
+ "items": [
+ {
+ "types": [
+ "callable"
+ ],
+ "variable": "$callback",
+ "content": "Callable eraser that accepts an email address and a page number, and returns an array with boolean values for whether items were removed or retained and any messages from the eraser, as well as if additional pages are available."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$exporter_friendly_name",
+ "content": "Translated user facing friendly name for the eraser."
+ }
+ ]
+ }
+ }
+ ]
+ }
}
]
}
@@ -29436,7 +29579,40 @@
"types": [
"array"
],
- "variable": "$response"
+ "variable": "$response",
+ "hash": {
+ "content": "The personal data for the given exporter and page number.",
+ "items": [
+ {
+ "types": [
+ "bool"
+ ],
+ "variable": "$items_removed",
+ "content": "Whether items were actually removed or not."
+ },
+ {
+ "types": [
+ "bool"
+ ],
+ "variable": "$items_retained",
+ "content": "Whether items were retained or not."
+ },
+ {
+ "types": [
+ "string[]"
+ ],
+ "variable": "$messages",
+ "content": "An array of messages to add to the personal data export file."
+ },
+ {
+ "types": [
+ "bool"
+ ],
+ "variable": "$done",
+ "content": "Whether the eraser is finished or not."
+ }
+ ]
+ }
},
{
"name": "param",
@@ -31122,7 +31298,103 @@
"types": [
"array"
],
- "variable": "$linkdata"
+ "variable": "$linkdata",
+ "hash": {
+ "content": "Elements that make up the link to insert.",
+ "items": [
+ {
+ "types": [
+ "int"
+ ],
+ "variable": "$link_id",
+ "content": "Optional. The ID of the existing link if updating."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$link_url",
+ "content": "The URL the link points to."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$link_name",
+ "content": "The title of the link."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$link_image",
+ "content": "Optional. A URL of an image."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$link_target",
+ "content": "Optional. The target element for the anchor tag."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$link_description",
+ "content": "Optional. A short description of the link."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$link_visible",
+ "content": "Optional. 'Y' means visible, anything else means not."
+ },
+ {
+ "types": [
+ "int"
+ ],
+ "variable": "$link_owner",
+ "content": "Optional. A user ID."
+ },
+ {
+ "types": [
+ "int"
+ ],
+ "variable": "$link_rating",
+ "content": "Optional. A rating for the link."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$link_rel",
+ "content": "Optional. A relationship of the link to you."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$link_notes",
+ "content": "Optional. An extended description of or notes on the link."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$link_rss",
+ "content": "Optional. A URL of an associated RSS feed."
+ },
+ {
+ "types": [
+ "int"
+ ],
+ "variable": "$link_category",
+ "content": "Optional. The term ID of the link category.<br>If empty, uses default link category."
+ }
+ ]
+ }
},
{
"name": "param",
@@ -33649,7 +33921,33 @@
"types": [
"array"
],
- "variable": "$args"
+ "variable": "$args",
+ "hash": {
+ "content": "Optional. Arguments for upgrading WordPress core. Default empty array.",
+ "items": [
+ {
+ "types": [
+ "bool"
+ ],
+ "variable": "$pre_check_md5",
+ "content": "Whether to check the file checksums before attempting the upgrade. Default true."
+ },
+ {
+ "types": [
+ "bool"
+ ],
+ "variable": "$attempt_rollback",
+ "content": "Whether to attempt to rollback the chances if there is a problem. Default false."
+ },
+ {
+ "types": [
+ "bool"
+ ],
+ "variable": "$do_rollback",
+ "content": "Whether to perform this \"upgrade\" as a rollback.<br>Default false."
+ }
+ ]
+ }
},
{
"name": "return",
@@ -46737,7 +47035,19 @@
"types": [
"array"
],
- "variable": "$args"
+ "variable": "$args",
+ "hash": {
+ "content": "Other arguments for upgrading multiple language packs. Default empty array.",
+ "items": [
+ {
+ "types": [
+ "bool"
+ ],
+ "variable": "$clear_update_cache",
+ "content": "Whether to clear the update cache when done.<br>Default true."
+ }
+ ]
+ }
},
{
"name": "return",
@@ -56463,7 +56773,19 @@
"types": [
"array"
],
- "variable": "$args"
+ "variable": "$args",
+ "hash": {
+ "content": "Optional. Other arguments for installing a plugin package. Default empty array.",
+ "items": [
+ {
+ "types": [
+ "bool"
+ ],
+ "variable": "$clear_update_cache",
+ "content": "Whether to clear the plugin updates cache if successful.<br>Default true."
+ }
+ ]
+ }
},
{
"name": "return",
@@ -56639,7 +56961,19 @@
"types": [
"array"
],
- "variable": "$args"
+ "variable": "$args",
+ "hash": {
+ "content": "Optional. Other arguments for upgrading a plugin package. Default empty array.",
+ "items": [
+ {
+ "types": [
+ "bool"
+ ],
+ "variable": "$clear_update_cache",
+ "content": "Whether to clear the plugin updates cache if successful.<br>Default true."
+ }
+ ]
+ }
},
{
"name": "return",
@@ -56841,7 +57175,19 @@
"types": [
"array"
],
- "variable": "$args"
+ "variable": "$args",
+ "hash": {
+ "content": "Optional. Other arguments for upgrading several plugins at once.",
+ "items": [
+ {
+ "types": [
+ "bool"
+ ],
+ "variable": "$clear_update_cache",
+ "content": "Whether to clear the plugin updates cache if successful. Default true."
+ }
+ ]
+ }
},
{
"name": "return",
@@ -59966,7 +60312,19 @@
"types": [
"array"
],
- "variable": "$args"
+ "variable": "$args",
+ "hash": {
+ "content": "Optional. Other arguments for installing a theme package. Default empty array.",
+ "items": [
+ {
+ "types": [
+ "bool"
+ ],
+ "variable": "$clear_update_cache",
+ "content": "Whether to clear the updates cache if successful.<br>Default true."
+ }
+ ]
+ }
},
{
"name": "return",
@@ -60128,7 +60486,19 @@
"types": [
"array"
],
- "variable": "$args"
+ "variable": "$args",
+ "hash": {
+ "content": "Optional. Other arguments for upgrading a theme. Default empty array.",
+ "items": [
+ {
+ "types": [
+ "bool"
+ ],
+ "variable": "$clear_update_cache",
+ "content": "Whether to clear the update cache if successful.<br>Default true."
+ }
+ ]
+ }
},
{
"name": "return",
@@ -60325,7 +60695,19 @@
"types": [
"array"
],
- "variable": "$args"
+ "variable": "$args",
+ "hash": {
+ "content": "Optional. Other arguments for upgrading several themes at once. Default empty array.",
+ "items": [
+ {
+ "types": [
+ "bool"
+ ],
+ "variable": "$clear_update_cache",
+ "content": "Whether to clear the update cache if successful.<br>Default true."
+ }
+ ]
+ }
},
{
"name": "return",
@@ -66688,7 +67070,40 @@
"types": [
"array"
],
- "variable": "$email"
+ "variable": "$email",
+ "hash": {
+ "content": "Array of email arguments that will be passed to wp_mail().",
+ "items": [
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$to",
+ "content": "The email recipient. An array of emails can be returned, as handled by wp_mail()."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$subject",
+ "content": "The email's subject."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$body",
+ "content": "The email message body."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$headers",
+ "content": "Any email headers, defaults to no headers."
+ }
+ ]
+ }
},
{
"name": "param",
@@ -67402,7 +67817,40 @@
"types": [
"array"
],
- "variable": "$email"
+ "variable": "$email",
+ "hash": {
+ "content": "Array of email arguments that will be passed to wp_mail().",
+ "items": [
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$to",
+ "content": "The email recipient. An array of emails can be returned, as handled by wp_mail()."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$subject",
+ "content": "The email's subject."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$body",
+ "content": "The email message body."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$headers",
+ "content": "Any email headers, defaults to no headers."
+ }
+ ]
+ }
},
{
"name": "param",
@@ -67821,7 +68269,40 @@
"types": [
"array"
],
- "variable": "$email"
+ "variable": "$email",
+ "hash": {
+ "content": "Array of email arguments that will be passed to wp_mail().",
+ "items": [
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$to",
+ "content": "The email recipient. An array of emails can be returned, as handled by wp_mail()."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$subject",
+ "content": "Email subject."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$body",
+ "content": "Email message body."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$headers",
+ "content": "Any email headers. Default empty."
+ }
+ ]
+ }
},
{
"name": "param",
@@ -68673,9 +69154,9 @@
},
{
"name": "return",
- "content": "string> Comment status HTML links keyed by view.",
+ "content": "Comment status HTML links keyed by view.",
"types": [
- "\\array<string,"
+ "array<string, string>"
]
}
]
@@ -68864,9 +69345,9 @@
},
{
"name": "return",
- "content": "string> Bulk action labels keyed by action name.",
+ "content": "Bulk action labels keyed by action name.",
"types": [
- "\\array<string,"
+ "array<string, string>"
]
}
]
@@ -69364,9 +69845,9 @@
"tags": [
{
"name": "return",
- "content": "string|array> The sortable columns.",
+ "content": "The sortable columns.",
"types": [
- "\\array<string,"
+ "array<string, string|array>"
]
}
]
@@ -70836,7 +71317,40 @@
"false",
"array"
],
- "variable": "$user_location"
+ "variable": "$user_location",
+ "hash": {
+ "content": "Stored location data for the user. false to pass no location.",
+ "items": [
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$description",
+ "content": "The name of the location"
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$latitude",
+ "content": "The latitude in decimal degrees notation, without the degree symbol. e.g.: 47.615200."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$longitude",
+ "content": "The longitude in decimal degrees notation, without the degree symbol. e.g.: -122.341100."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$country",
+ "content": "The ISO 3166-1 alpha-2 country code. e.g.: BR"
+ }
+ ]
+ }
}
]
},
@@ -72070,7 +72584,104 @@
"types": [
"array"
],
- "variable": "$args"
+ "variable": "$args",
+ "hash": {
+ "content": "The debug information to be added to the core information page.<br>This is an associative multi-dimensional array, up to three levels deep.<br>The topmost array holds the sections, keyed by section ID.",
+ "items": [
+ {
+ "types": [
+ "array"
+ ],
+ "variable": "...$0",
+ "content": "",
+ "hash": {
+ "content": "Each section has a <code>$fields<\/code> associative array (see below), and each <code>$value<\/code> in <code>$fields<\/code> can be another associative array of name\/value pairs when there is more structured data to display.",
+ "items": [
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$label",
+ "content": "Required. The title for this section of the debug output."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$description",
+ "content": "Optional. A description for your information section which may contain basic HTML markup, inline tags only as it is outputted in a paragraph."
+ },
+ {
+ "types": [
+ "bool"
+ ],
+ "variable": "$show_count",
+ "content": "Optional. If set to <code>true<\/code>, the amount of fields will be included in the title for this section. Default false."
+ },
+ {
+ "types": [
+ "bool"
+ ],
+ "variable": "$private",
+ "content": "Optional. If set to <code>true<\/code>, the section and all associated fields will be excluded from the copied data. Default false."
+ },
+ {
+ "types": [
+ "array"
+ ],
+ "variable": "$fields",
+ "content": "",
+ "hash": {
+ "content": "Required. An associative array containing the fields to be displayed in the section, keyed by field ID.",
+ "items": [
+ {
+ "types": [
+ "array"
+ ],
+ "variable": "...$0",
+ "content": "",
+ "hash": {
+ "content": "An associative array containing the data to be displayed for the field.",
+ "items": [
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$label",
+ "content": "Required. The label for this piece of information."
+ },
+ {
+ "types": [
+ "mixed"
+ ],
+ "variable": "$value",
+ "content": "Required. The output that is displayed for this field.<br>Text should be translated. Can be an associative array that is displayed as name\/value pairs.<br>Accepted types: <code>string|int|float|(string|int|float)[]<\/code>."
+ },
+ {
+ "types": [
+ "string"
+ ],
+ "variable": "$debug",
+ "content": "Optional. The output that is used for this field when the user copies the data. It should be more concise and not translated. If not set, the content of <code>$value<\/code> is used. Note that the array keys are used as labels for the copied data."
+ },
+ {
+ "types": [
+ "bool"
+ ],
+ "variable": "$private",
+ "content": "Optional. If set to <code>true<\/code>, the field will be excluded from the copied data, allowing you to show, for example, API keys here. Default false."
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
}
]
}
@@ -72098,9 +72709,9 @@
},
{
"name": "return",
- "content": "string|array> The debug data for the Info screen.",
+ "content": "The debug data for the Info screen.",
"types": [
- "\\array<string,"
+ "array<string, string|array>"
]
}
]
@@ -72456,9 +73067,9 @@
},
{
"name": "return",
- "content": "string|bool|array> The drop-ins debug data.",
+ "content": "The drop-ins debug data.",
"types": [
- "\\array<string,"
+ "array<string, string|bool|array>"
]
}
]
@@ -72524,9 +73135,9 @@
},
{
"name": "return",
- "content": "string|array> The server-related debug data.",
+ "content": "The server-related debug data.",
"types": [
- "\\array<string,"
+ "array<string, string|array>"
]
}
]
@@ -73153,9 +73764,9 @@
},
{
"name": "return",
- "content": "string|array> The media handling debug data.",
+ "content": "The media handling debug data.",
"types": [
- "\\array<string,"
+ "array<string, string|array>"
]
}
]
@@ -73662,9 +74273,9 @@
},
{
"name": "return",
- "content": "string|bool|array> The must-use plugins debug data.",
+ "content": "The must-use plugins debug data.",
"types": [
- "\\array<string,"
+ "array<string, string|bool|array>"
]
}
]
@@ -73889,9 +74500,9 @@
},
{
"name": "return",
- "content": "string|bool|array> The active plugins debug data.",
+ "content": "The active plugins debug data.",
"types": [
- "\\array<string,"
+ "array<string, string|bool|array>"
]
}
]
@@ -73936,9 +74547,9 @@
},
{
"name": "return",
- "content": "string|bool|array> The inactive plugins debug data.",
+ "content": "The inactive plugins debug data.",
"types": [
- "\\array<string,"
+ "array<string, string|bool|array>"
]
}
]
@@ -73983,9 +74594,9 @@
},
{
"name": "return",
- "content": "array<string, array<string, string>>> The raw plugin debug data for active and inactive plugins.",
+ "content": "The raw plugin debug data for active and inactive plugins.",
"types": [
- "\\array<string,"
+ "array<string, array<string, array<string, string>>>"
]
}
]
@@ -74225,9 +74836,9 @@
},
{
"name": "return",
- "content": "string|array> The active theme debug data.",
+ "content": "The active theme debug data.",
"types": [
- "\\array<string,"
+ "array<string, string|array>"
]
}
]
@@ -74455,9 +75066,9 @@
},
{
"name": "return",
- "content": "string|array> The parent theme debug data.",
+ "content": "The parent theme debug data.",
"types": [
- "\\array<string,"
+ "array<string, string|array>"
]
}
]
@@ -74657,9 +75268,9 @@
},
{
"name": "return",
- "content": "string|bool|array> The inactive themes debug data.",
+ "content": "The inactive themes debug data.",
"types": [
- "\\array<string,"
+ "array<string, string|bool|array>"
]
}
] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Read a tag's type with phpstan/phpdoc-parser instead of phpDocumentor's whitespace split, and export WordPress hash notation as structured data.
reflection-docblock 2.x splits a tag's content on the first whitespace and its type on every
|. Today@param array<string, mixed> $l10n The data itself.(fromwp_localize_script()) exports:{ "types": ["\\array<string,"], "variable": "", "content": "mixed> $l10n The data itself." }With this PR:
{ "types": ["array<string, mixed>"], "variable": "$l10n", "content": "The data itself." }Types
phpDocumentor still splits the DocBlock into descriptions and tags. For
@param,@var,@property*,@returnand@throws,TypeParserreads the type at the start of the tag content, followed by the variable and description.@methodis unchanged.The
typescontract is unchanged: one string per top-level union member (UnionTypeNode->types). Each member keeps its source spelling, with class names resolved against the DocBlock's namespace andusealiases as phpDocumentor resolves them. Resolution now also applies inside generics, shapes, callables, intersections andFoo::CONST_*patterns. Keywords, PHPStan pseudo-types (list,class-string,non-empty-string,int<0, max>, …), shape keys and the DocBlock's own@templatenames are not prefixed. Members are printed from the original tokens, with resolved class names substituted, not from the AST's string form. A multi-line array shape is printed on one line.wp_ai_client_prompt()'s$prompt(#268) now exports:If
TypeParserthrows, or the parsed type is not followed by whitespace or the end of the content ((bool): true,string|null.), the tag is exported as it is today. The same applies when whitespace at the top level separates a type from a following bracket (string <code>,array <int>).Hash notation
When a typed tag's description is a
{ … }hash, the tag gets ahashkey besidecontent.contentis unchanged byte for byte, so the theme's text parsing keeps working until it readshash. A@typeline ending in{opens a nested hash, closed by a line holding only}.@return array $results {is also read. Hashes with unbalanced braces get nohashkey. In 7.1.2, the only case is thewp_privacy_personal_data_email_contentdocblock, which lacks its closing}. Part of #119; rendering fromhashis theme work.Import
The importer stores tags with
update_post_meta()and applies no KSES there, so generics andhashare stored as exported. A test covers this.make_args_safe()does runwp_filter_kses(), but only on the template-sidewp_parser_get_argumentsfilter. Namespace separators are still unslashed on import (#287).Corpus diff (WordPress 7.1.2)
Local run of
tools/export-corpus.phpon master and on this branch: 1,429 hunks. 822 tag entries change type, content or variable; 669 gainhash.list<int|string>,(string|null)[][]) are no longer split.\array<string,string>,\SimplePie\?string,\WpOrg\Requests\iterable.\WordPress\AiClient\Builders\list<Message>becomeslist<\WordPress\AiClient\Messages\DTO\Message>. This also fixes wrong resolutions:\SimplePie\Cache\InvalidArgumentException&Throwablebecomes\Psr\SimpleCache\InvalidArgumentException&\Throwableper the file'suse, and\?WP_Errorbecomes?\WP_Error.Type &$x) recover their variable.-1|0|1, was exported as\SimplePie\-1,\SimplePie\1, dropping0; it is now three members.@param, 137 on@returnand 3 on@var.No exported type in the corpus has unbalanced brackets after this change. No variable is lost, and every
contentchange removes a leaked type fragment.Known limitations:
@templatenames declared on a class are not visible from a method or property DocBlock. SimplePie'sPsr7Compatibleexports as\SimplePie\HTTP\Psr7Compatibleinside a conditional type, as it is resolved on master.parse_files()strips the leading\only from a whole bare name.WP_Poststays bare, whilelist<\WP_Post>and?\WP_Postkeep the backslash.Relation to other PRs
Supersedes #270, which fixes the type split with a hand-written bracket scanner. This PR uses PHPStan's grammar instead, keeps the current export contract, and leaves theme rendering to wporg-developer.
Unlike #247, this PR does not move the parser onto php-parser 5 or replace reflection-docblock. It adds
phpstan/phpdoc-parser^2.3 (PHP ^7.4 || ^8.0, matchingcomposer.json) and replaces only how a tag's type, variable and description are read.Rendering generics on developer.wordpress.org also needs WordPress/wporg-developer#598, which escapes types in the theme.
Testing
npm testin wp-env passed on PHP 7.4.33 and 8.4.26 (193 tests, 452 assertions). New tests cover:@returngenerics, by-reference variables and untyped parametersTypeParserrejectswp_localize_script(),wp_ai_client_prompt()andwp_register_ability()DocBlocks from core$0,...$0, unnamed entries, continuation lines, generics with whitespace and unbalanced braceshashinto_wp-parser_tagsFixes #304
Fixes #268
Part of #119