Modernize parser to use phpstan/phpdoc-parser
Summary
Complete modernization of the WordPress PHPDoc parser from PHP 5.4/legacy libraries to phpstan/phpdoc-parser while maintaining 100% backward compatibility.
- Complete API compatibility maintained
- Modern PHP 8 support
- Increased test coverage
Changes Made
Dependencies
- PHP:
>=5.4→>=8.1 - Parser:
phpdocumentor/reflection v3→nikic/php-parser v5withphpstan/phpdoc-parser v2 - Testing: PHPUnit
v7→v9
Architecture
- File reflector: Complete rewrite using PHPParser NodeVisitorAbstract for AST traversal
- Modern parsing: Replace legacy reflection with AST-based parsing for improved accuracy
- Backward compatibility: Maintained through
runner.phpbridge layer
AI disclosure
These changes were generated primarily by an AI coding assistant. I have yet to manually review the changes thoroughly.
Testing in comparison to #248 this seems to work pretty well, although it looks like it's missing a few fields still, and presents some data in a different manner. The differences are minimal and easily fixable.
It's worth noting that some of these diffs might be irrelevant, as the importer may not need the field to exist.
For example, two random diffs I pulled from the 100MB generated JSONs (can be viewed via https://jsondiff.com/ )
- {"classes":[{"abstract":true,"doc":{"description":"Base class for database-based caches","long_description":"","tags":[{"content":"SimplePie","name":"package"},{"content":"Caching","name":"subpackage"},{"content":"since SimplePie 1.8.0, use implementation of \u0026quot;Psr\\SimpleCache\\CacheInterface\u0026quot; instead","description":"since SimplePie 1.8.0, use implementation of \u0026quot;Psr\\SimpleCache\\CacheInterface\u0026quot; instead","name":"deprecated"}]},"end_line":119,"extends":"","final":false,"implements":["\\SimplePie\\Cache\\Base"],"line":54,"methods":[{"abstract":false,"aliases":[],"arguments":[{"default":null,"name":"$data","type":""}],"doc":{"description":"Helper for database conversion","long_description":"\u003cp\u003eConverts a given {@see SimplePie} object into data to be stored\u003c/p\u003e","tags":[{"content":"","name":"param","types":["\\SimplePie\\SimplePie"],"variable":"$data"},{"content":"First item is the serialized data for storage, second item is the unique ID for this item","name":"return","types":["array"]}]},"end_line":118,"final":false,"line":64,"name":"prepare_simplepie_object_for_cache","namespace":"SimplePie\\Cache","static":true,"uses":{"functions":[{"end_line":74,"line":74,"name":"count"},{"end_line":74,"line":74,"name":"count"},{"end_line":117,"line":117,"name":"serialize"}],"methods":[{"class":"$data","end_line":66,"line":66,"name":"get_items","static":false},{"class":"$item","end_line":71,"line":71,"name":"get_id","static":false},{"class":"$item","end_line":77,"line":77,"name":"get_id","static":false}]},"visibility":"protected"}],"name":"DB","namespace":"SimplePie\\Cache","properties":[]}],"file":{"description":"SimplePie","long_description":"\u003cp\u003eA PHP-Based RSS and Atom Feed Framework.\u003cbr\u003eTakes the hard work out of managing a complete RSS/Atom solution.\u003c/p\u003e \u003cp\u003eCopyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors All rights reserved.\u003c/p\u003e \u003cp\u003eRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\u003c/p\u003e \u003cpre\u003e\u003ccode\u003e* Redistributions of source code must retain the above copyright notice, this list of\n conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice, this list\n of conditions and the following disclaimer in the documentation and/or other materials\n provided with the distribution.\n\n* Neither the name of the SimplePie Team nor the names of its contributors may be used\n to endorse or promote products derived from this software without specific prior\n written permission.\u003c/code\u003e\u003c/pre\u003e \u003cp\u003eTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \u0026quot;AS IS\u0026quot; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\u003c/p\u003e","tags":[{"content":"SimplePie","name":"package"},{"content":"2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue","name":"copyright"},{"content":"Ryan Parman","name":"author"},{"content":"Sam Sneddon","name":"author"},{"content":"Ryan McCue","name":"author"},{"content":"SimplePie","link":"http://simplepie.org/","name":"link"},{"content":"\u003ca href=\"http://www.opensource.org/licenses/bsd-license.php\"\u003ehttp://www.opensource.org/licenses/bsd-license.php\u003c/a\u003e BSD License","name":"license"}]},"path":"src/wp-includes/SimplePie/src/Cache/DB.php","root":"/var/www/html/core","uses":{"functions":[{"end_line":121,"line":121,"name":"class_alias"}]}}
+ {"classes":[{"doc":{"description":"Base class for database-based caches","long_description":"","tags":[{"content":"SimplePie","name":"package"},{"content":"Caching","name":"subpackage"},{"content":"since SimplePie 1.8.0, use implementation of \"Psr\\SimpleCache\\CacheInterface\" instead","name":"deprecated"}]},"end_line":119,"line":54,"methods":[{"abstract":false,"arguments":[{"default":null,"name":"$data","type":""}],"doc":{"description":"Helper for database conversion","long_description":"\u003cp\u003eConverts a given {@see SimplePie} object into data to be stored\u003c/p\u003e","tags":[{"content":"$data","name":"param","types":["\\SimplePie\\SimplePie"]},{"content":"First item is the serialized data for storage, second item is the unique ID for this item","name":"return","types":["array"]}]},"end_line":118,"final":false,"hooks":[],"line":64,"name":"prepare_simplepie_object_for_cache","static":true,"uses":{"functions":[{"end_line":74,"line":74,"name":"count"},{"end_line":74,"line":74,"name":"count"},{"end_line":117,"line":117,"name":"serialize"}],"methods":[{"class":"$data","end_line":66,"line":66,"name":"get_items","static":false},{"class":"$item","end_line":71,"line":71,"name":"get_id","static":false},{"class":"$item","end_line":77,"line":77,"name":"get_id","static":false}]},"visibility":"protected"}],"name":"DB","namespace":"SimplePie\\Cache","properties":[],"uses":[]}],"file":{"description":"SimplePie","long_description":"\u003cp\u003eA PHP-Based RSS and Atom Feed Framework. Takes the hard work out of managing a complete RSS/Atom solution. Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the SimplePie Team nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\u003c/p\u003e","tags":[{"content":"SimplePie","name":"package"},{"content":"2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue","name":"copyright"},{"content":"Ryan Parman","name":"author"},{"content":"Sam Sneddon","name":"author"},{"content":"Ryan McCue","name":"author"},{"content":"http://simplepie.org/ SimplePie","name":"link"},{"content":"http://www.opensource.org/licenses/bsd-license.php BSD License","name":"license"}]},"path":"src/wp-includes/SimplePie/src/Cache/DB.php","root":"/var/www/html/core","uses":{"functions":[{"end_line":121,"line":121,"name":"class_alias"}]}}
- {"file":{"description":"Twenty Fifteen Customizer functionality","long_description":"","tags":[{"content":"WordPress","name":"package"},{"content":"Twenty_Fifteen","name":"subpackage"},{"content":"Twenty Fifteen 1.0","description":"Twenty Fifteen 1.0","name":"since"}]},"functions":[{"aliases":[],"arguments":[{"default":null,"name":"$wp_customize","type":""}],"doc":{"description":"Adds postMessage support for site title and description for the Customizer.","long_description":"","tags":[{"content":"Twenty Fifteen 1.0","description":"Twenty Fifteen 1.0","name":"since"},{"content":"Customizer object.","name":"param","types":["\\WP_Customize_Manager"],"variable":"$wp_customize"}]},"end_line":112,"hooks":[],"line":17,"name":"twentyfifteen_customize_register","namespace":"global","uses":{"functions":[{"end_line":18,"line":18,"name":"twentyfifteen_get_color_scheme"},{"end_line":55,"line":55,"name":"__"},{"end_line":58,"line":58,"name":"twentyfifteen_get_color_scheme_choices"},{"end_line":78,"line":78,"name":"__"},{"end_line":79,"line":79,"name":"__"},{"end_line":103,"line":103,"name":"__"},{"end_line":104,"line":104,"name":"__"},{"end_line":111,"line":111,"name":"__"}],"methods":[{"class":"$wp_customize","end_line":20,"line":20,"name":"get_setting","static":false},{"class":"$wp_customize","end_line":21,"line":21,"name":"get_setting","static":false},{"class":"$wp_customize-\u003eselective_refresh","end_line":31,"line":24,"name":"add_partial","static":false},{"class":"$wp_customize-\u003eselective_refresh","end_line":39,"line":32,"name":"add_partial","static":false},{"class":"$wp_customize","end_line":50,"line":43,"name":"add_setting","static":false},{"class":"$wp_customize","end_line":61,"line":52,"name":"add_control","static":false},{"class":"$wp_customize","end_line":71,"line":64,"name":"add_setting","static":false},{"class":"$wp_customize","end_line":83,"line":73,"name":"add_control","static":false},{"class":"\\WP_Customize_Color_Control","end_line":82,"line":74,"name":"__construct","static":false},{"class":"$wp_customize","end_line":86,"line":86,"name":"remove_control","static":false},{"class":"$wp_customize","end_line":96,"line":89,"name":"add_setting","static":false},{"class":"$wp_customize","end_line":108,"line":98,"name":"add_control","static":false},{"class":"\\WP_Customize_Color_Control","end_line":107,"line":99,"name":"__construct","static":false},{"class":"$wp_customize","end_line":111,"line":111,"name":"get_section","static":false}]}},{"aliases":[],"arguments":[],"doc":{"description":"Renders the site title for the selective refresh partial.","long_description":"","tags":[{"content":"Twenty Fifteen 1.5","description":"Twenty Fifteen 1.5","name":"since"},{"content":"","name":"see","refers":"twentyfifteen_customize_register()"},{"content":"","name":"return","types":["void"]}]},"end_line":126,"hooks":[],"line":124,"name":"twentyfifteen_customize_partial_blogname","namespace":"global","uses":{"functions":[{"end_line":125,"line":125,"name":"bloginfo"}]}},{"aliases":[],"arguments":[],"doc":{"description":"Renders the site tagline for the selective refresh partial.","long_description":"","tags":[{"content":"Twenty Fifteen 1.5","description":"Twenty Fifteen 1.5","name":"since"},{"content":"","name":"see","refers":"twentyfifteen_customize_register()"},{"content":"","name":"return","types":["void"]}]},"end_line":139,"hooks":[],"line":137,"name":"twentyfifteen_customize_partial_blogdescription","namespace":"global","uses":{"functions":[{"end_line":138,"line":138,"name":"bloginfo"}]}},{"aliases":[],"arguments":[],"doc":{"description":"Registers color schemes for Twenty Fifteen.","long_description":"\u003cp\u003eCan be filtered with {@see 'twentyfifteen_color_schemes'}.\u003c/p\u003e \u003cp\u003eThe order of colors in a colors array:\u003c/p\u003e \u003col\u003e \u003cli\u003eMain Background Color.\u003c/li\u003e \u003cli\u003eSidebar Background Color.\u003c/li\u003e \u003cli\u003eBox Background Color.\u003c/li\u003e \u003cli\u003eMain Text and Link Color.\u003c/li\u003e \u003cli\u003eSidebar Text and Link Color.\u003c/li\u003e \u003cli\u003eMeta Box Background Color.\u003c/li\u003e \u003c/ol\u003e","tags":[{"content":"Twenty Fifteen 1.0","description":"Twenty Fifteen 1.0","name":"since"},{"content":"An associative array of color scheme options.","name":"return","types":["array"]}]},"end_line":251,"hooks":[{"arguments":["array('default' =\u003e array('label' =\u003e __('Default', 'twentyfifteen'), 'colors' =\u003e array('#f1f1f1', '#ffffff', '#ffffff', '#333333', '#333333', '#f7f7f7')), 'dark' =\u003e array('label' =\u003e __('Dark', 'twentyfifteen'), 'colors' =\u003e array('#111111', '#202020', '#202020', '#bebebe', '#bebebe', '#1b1b1b')), 'yellow' =\u003e array('label' =\u003e __('Yellow', 'twentyfifteen'), 'colors' =\u003e array('#f4ca16', '#ffdf00', '#ffffff', '#111111', '#111111', '#f1f1f1')), 'pink' =\u003e array('label' =\u003e __('Pink', 'twentyfifteen'), 'colors' =\u003e array('#ffe5d1', '#e53b51', '#ffffff', '#352712', '#ffffff', '#f1f1f1')), 'purple' =\u003e array('label' =\u003e __('Purple', 'twentyfifteen'), 'colors' =\u003e array('#674970', '#2e2256', '#ffffff', '#2e2256', '#ffffff', '#f1f1f1')), 'blue' =\u003e array('label' =\u003e __('Blue', 'twentyfifteen'), 'colors' =\u003e array('#e9f2f9', '#55c3dc', '#ffffff', '#22313f', '#ffffff', '#f1f1f1')))"],"doc":{"description":"Filters the color schemes registered for use with Twenty Fifteen.","long_description":"\u003cp\u003eThe default schemes include 'default', 'dark', 'yellow', 'pink', 'purple', and 'blue'.\u003c/p\u003e","tags":[{"content":"Twenty Fifteen 1.0","description":"Twenty Fifteen 1.0","name":"since"},{"content":"{ Associative array of color schemes data.\u003cbr\u003e @type array $slug { Associative array of information for setting up the color scheme.\u003cbr\u003e @type string $label Color scheme label.\u003cbr\u003e @type array $colors HEX codes for default colors prepended with a hash symbol ('#').\u003cbr\u003e Colors are defined in the following order: Main background, sidebar background, box background, main text and link, sidebar text and link, meta box background.\u003cbr\u003e } }","name":"param","types":["array"],"variable":"$schemes"}]},"end_line":250,"line":180,"name":"twentyfifteen_color_schemes","type":"filter"}],"line":158,"name":"twentyfifteen_get_color_schemes","namespace":"global","uses":{"functions":[{"end_line":250,"line":180,"name":"apply_filters"},{"end_line":184,"line":184,"name":"__"},{"end_line":195,"line":195,"name":"__"},{"end_line":206,"line":206,"name":"__"},{"end_line":217,"line":217,"name":"__"},{"end_line":228,"line":228,"name":"__"},{"end_line":239,"line":239,"name":"__"}]}},{"aliases":[],"arguments":[],"doc":{"description":"Gets the current Twenty Fifteen color scheme.","long_description":"","tags":[{"content":"Twenty Fifteen 1.0","description":"Twenty Fifteen 1.0","name":"since"},{"content":"An associative array of either the current or default color scheme hex values.","name":"return","types":["array"]}]},"end_line":270,"hooks":[],"line":261,"name":"twentyfifteen_get_color_scheme","namespace":"global","uses":{"functions":[{"end_line":262,"line":262,"name":"get_theme_mod"},{"end_line":263,"line":263,"name":"twentyfifteen_get_color_schemes"},{"end_line":265,"line":265,"name":"array_key_exists"}]}},{"aliases":[],"arguments":[],"doc":{"description":"Returns an array of color scheme choices registered for Twenty Fifteen.","long_description":"","tags":[{"content":"Twenty Fifteen 1.0","description":"Twenty Fifteen 1.0","name":"since"},{"content":"Array of color schemes.","name":"return","types":["array"]}]},"end_line":290,"hooks":[],"line":281,"name":"twentyfifteen_get_color_scheme_choices","namespace":"global","uses":{"functions":[{"end_line":282,"line":282,"name":"twentyfifteen_get_color_schemes"}]}},{"aliases":[],"arguments":[{"default":null,"name":"$value","type":""}],"doc":{"description":"Sanitization callback for color schemes.","long_description":"","tags":[{"content":"Twenty Fifteen 1.0","description":"Twenty Fifteen 1.0","name":"since"},{"content":"Color scheme name value.","name":"param","types":["string"],"variable":"$value"},{"content":"Color scheme name.","name":"return","types":["string"]}]},"end_line":310,"hooks":[],"line":302,"name":"twentyfifteen_sanitize_color_scheme","namespace":"global","uses":{"functions":[{"end_line":303,"line":303,"name":"twentyfifteen_get_color_scheme_choices"},{"end_line":305,"line":305,"name":"array_key_exists"}]}},{"aliases":[],"arguments":[],"doc":{"description":"Enqueues front-end CSS for color scheme.","long_description":"","tags":[{"content":"Twenty Fifteen 1.0","description":"Twenty Fifteen 1.0","name":"since"},{"content":"","name":"see","refers":"wp_add_inline_style()"}]},"end_line":351,"hooks":[],"line":320,"name":"twentyfifteen_color_scheme_css","namespace":"global","uses":{"functions":[{"end_line":321,"line":321,"name":"get_theme_mod"},{"end_line":328,"line":328,"name":"twentyfifteen_get_color_scheme"},{"end_line":331,"line":331,"name":"twentyfifteen_hex2rgb"},{"end_line":332,"line":332,"name":"twentyfifteen_hex2rgb"},{"end_line":338,"line":338,"name":"vsprintf"},{"end_line":339,"line":339,"name":"vsprintf"},{"end_line":340,"line":340,"name":"vsprintf"},{"end_line":342,"line":342,"name":"vsprintf"},{"end_line":343,"line":343,"name":"vsprintf"},{"end_line":344,"line":344,"name":"vsprintf"},{"end_line":348,"line":348,"name":"twentyfifteen_get_color_scheme_css"},{"end_line":350,"line":350,"name":"wp_add_inline_style"}]}},{"aliases":[],"arguments":[],"doc":{"description":"Binds JS listener to make Customizer color_scheme control.","long_description":"\u003cp\u003ePasses color scheme data as colorScheme global.\u003c/p\u003e","tags":[{"content":"Twenty Fifteen 1.0","description":"Twenty Fifteen 1.0","name":"since"}]},"end_line":364,"hooks":[],"line":361,"name":"twentyfifteen_customize_control_js","namespace":"global","uses":{"functions":[{"end_line":362,"line":362,"name":"wp_enqueue_script"},{"end_line":362,"line":362,"name":"get_template_directory_uri"},{"end_line":363,"line":363,"name":"wp_localize_script"},{"end_line":363,"line":363,"name":"twentyfifteen_get_color_schemes"}]}},{"aliases":[],"arguments":[],"doc":{"description":"Binds JS handlers to make the Customizer preview reload changes asynchronously.","long_description":"","tags":[{"content":"Twenty Fifteen 1.0","description":"Twenty Fifteen 1.0","name":"since"}]},"end_line":374,"hooks":[],"line":372,"name":"twentyfifteen_customize_preview_js","namespace":"global","uses":{"functions":[{"end_line":373,"line":373,"name":"wp_enqueue_script"},{"end_line":373,"line":373,"name":"get_template_directory_uri"}]}},{"aliases":[],"arguments":[{"default":null,"name":"$colors","type":""}],"doc":{"description":"Returns CSS for the color schemes.","long_description":"","tags":[{"content":"Twenty Fifteen 1.0","description":"Twenty Fifteen 1.0","name":"since"},{"content":"Color scheme colors.","name":"param","types":["array"],"variable":"$colors"},{"content":"Color scheme CSS.","name":"return","types":["string"]}]},"end_line":770,"hooks":[],"line":385,"name":"twentyfifteen_get_color_scheme_css","namespace":"global","uses":{"functions":[{"end_line":402,"line":386,"name":"wp_parse_args"}]}},{"aliases":[],"arguments":[],"doc":{"description":"Outputs an Underscore template for generating CSS for the color scheme.","long_description":"\u003cp\u003eThe template generates the css dynamically for instant display in the Customizer preview.\u003c/p\u003e","tags":[{"content":"Twenty Fifteen 1.0","description":"Twenty Fifteen 1.0","name":"since"}]},"end_line":800,"hooks":[],"line":780,"name":"twentyfifteen_color_scheme_css_template","namespace":"global","uses":{"functions":[{"end_line":797,"line":797,"name":"twentyfifteen_get_color_scheme_css"}]}}],"path":"src/wp-content/themes/twentyfifteen/inc/customizer.php","root":"/var/www/html/core","uses":{"functions":[{"end_line":113,"line":113,"name":"add_action"},{"end_line":253,"line":253,"name":"function_exists"},{"end_line":273,"line":273,"name":"function_exists"},{"end_line":293,"line":293,"name":"function_exists"},{"end_line":352,"line":352,"name":"add_action"},{"end_line":365,"line":365,"name":"add_action"},{"end_line":375,"line":375,"name":"add_action"},{"end_line":801,"line":801,"name":"add_action"}]}}
+ {"file":{"description":"Twenty Fifteen Customizer functionality","long_description":"","tags":[{"content":"WordPress","name":"package"},{"content":"Twenty_Fifteen","name":"subpackage"},{"content":"Twenty Fifteen 1.0","name":"since"}]},"functions":[{"arguments":[{"default":null,"name":"$wp_customize","type":""}],"doc":{"description":"Adds postMessage support for site title and description for the Customizer.","long_description":"","tags":[{"content":"Twenty Fifteen 1.0","name":"since"},{"content":"Customizer object.","name":"param","types":["WP_Customize_Manager"],"variable":"$wp_customize"}]},"end_line":112,"hooks":[],"line":17,"name":"twentyfifteen_customize_register","namespace":null,"uses":{"functions":[{"end_line":18,"line":18,"name":"twentyfifteen_get_color_scheme"},{"end_line":55,"line":55,"name":"__"},{"end_line":58,"line":58,"name":"twentyfifteen_get_color_scheme_choices"},{"end_line":78,"line":78,"name":"__"},{"end_line":79,"line":79,"name":"__"},{"end_line":103,"line":103,"name":"__"},{"end_line":104,"line":104,"name":"__"},{"end_line":111,"line":111,"name":"__"}],"methods":[{"class":"$wp_customize","end_line":20,"line":20,"name":"get_setting","static":false},{"class":"$wp_customize","end_line":21,"line":21,"name":"get_setting","static":false},{"class":"$wp_customize-\u003eselective_refresh","end_line":24,"line":24,"name":"add_partial","static":false},{"class":"$wp_customize-\u003eselective_refresh","end_line":32,"line":32,"name":"add_partial","static":false},{"class":"$wp_customize","end_line":43,"line":43,"name":"add_setting","static":false},{"class":"$wp_customize","end_line":52,"line":52,"name":"add_control","static":false},{"class":"$wp_customize","end_line":64,"line":64,"name":"add_setting","static":false},{"class":"$wp_customize","end_line":73,"line":73,"name":"add_control","static":false},{"class":"\\WP_Customize_Color_Control","end_line":74,"line":74,"name":"__construct","static":false},{"class":"$wp_customize","end_line":86,"line":86,"name":"remove_control","static":false},{"class":"$wp_customize","end_line":89,"line":89,"name":"add_setting","static":false},{"class":"$wp_customize","end_line":98,"line":98,"name":"add_control","static":false},{"class":"\\WP_Customize_Color_Control","end_line":99,"line":99,"name":"__construct","static":false},{"class":"$wp_customize","end_line":111,"line":111,"name":"get_section","static":false}]}},{"arguments":[],"doc":{"description":"Renders the site title for the selective refresh partial.","long_description":"","tags":[{"content":"Twenty Fifteen 1.5","name":"since"},{"content":"twentyfifteen_customize_register()","name":"see"},{"content":"void","name":"return"}]},"end_line":126,"hooks":[],"line":124,"name":"twentyfifteen_customize_partial_blogname","namespace":null,"uses":{"functions":[{"end_line":125,"line":125,"name":"bloginfo"}]}},{"arguments":[],"doc":{"description":"Renders the site tagline for the selective refresh partial.","long_description":"","tags":[{"content":"Twenty Fifteen 1.5","name":"since"},{"content":"twentyfifteen_customize_register()","name":"see"},{"content":"void","name":"return"}]},"end_line":139,"hooks":[],"line":137,"name":"twentyfifteen_customize_partial_blogdescription","namespace":null,"uses":{"functions":[{"end_line":138,"line":138,"name":"bloginfo"}]}},{"arguments":[],"doc":{"description":"Registers color schemes for Twenty Fifteen.","long_description":"\u003cp\u003eCan be filtered with {@see 'twentyfifteen_color_schemes'}. The order of colors in a colors array: 1. Main Background Color. 2. Sidebar Background Color. 3. Box Background Color. 4. Main Text and Link Color. 5. Sidebar Text and Link Color. 6. Meta Box Background Color.\u003c/p\u003e","tags":[{"content":"Twenty Fifteen 1.0","name":"since"},{"content":"An associative array of color scheme options.","name":"return","types":["array"]}]},"end_line":251,"hooks":[{"arguments":["array('default' =\u003e array('label' =\u003e __('Default', 'twentyfifteen'), 'colors' =\u003e array('#f1f1f1', '#ffffff', '#ffffff', '#333333', '#333333', '#f7f7f7')), 'dark' =\u003e array('label' =\u003e __('Dark', 'twentyfifteen'), 'colors' =\u003e array('#111111', '#202020', '#202020', '#bebebe', '#bebebe', '#1b1b1b')), 'yellow' =\u003e array('label' =\u003e __('Yellow', 'twentyfifteen'), 'colors' =\u003e array('#f4ca16', '#ffdf00', '#ffffff', '#111111', '#111111', '#f1f1f1')), 'pink' =\u003e array('label' =\u003e __('Pink', 'twentyfifteen'), 'colors' =\u003e array('#ffe5d1', '#e53b51', '#ffffff', '#352712', '#ffffff', '#f1f1f1')), 'purple' =\u003e array('label' =\u003e __('Purple', 'twentyfifteen'), 'colors' =\u003e array('#674970', '#2e2256', '#ffffff', '#2e2256', '#ffffff', '#f1f1f1')), 'blue' =\u003e array('label' =\u003e __('Blue', 'twentyfifteen'), 'colors' =\u003e array('#e9f2f9', '#55c3dc', '#ffffff', '#22313f', '#ffffff', '#f1f1f1')))"],"doc":{"description":"Filters the color schemes registered for use with Twenty Fifteen. The default schemes include 'default', 'dark', 'yellow', 'pink', 'purple', and 'blue'. Associative array of color schemes data. Associative array of information for setting up the color scheme. Colors are defined in the following order: Main background, sidebar background, box background, main text and link, sidebar text and link, meta box background. } }","long_description":"","tags":[]},"end_line":180,"line":180,"name":"twentyfifteen_color_schemes","type":"filter"}],"line":158,"name":"twentyfifteen_get_color_schemes","namespace":null,"uses":{"functions":[{"end_line":180,"line":180,"name":"apply_filters"},{"end_line":184,"line":184,"name":"__"},{"end_line":195,"line":195,"name":"__"},{"end_line":206,"line":206,"name":"__"},{"end_line":217,"line":217,"name":"__"},{"end_line":228,"line":228,"name":"__"},{"end_line":239,"line":239,"name":"__"}],"hooks":[{"arguments":["array('default' =\u003e array('label' =\u003e __('Default', 'twentyfifteen'), 'colors' =\u003e array('#f1f1f1', '#ffffff', '#ffffff', '#333333', '#333333', '#f7f7f7')), 'dark' =\u003e array('label' =\u003e __('Dark', 'twentyfifteen'), 'colors' =\u003e array('#111111', '#202020', '#202020', '#bebebe', '#bebebe', '#1b1b1b')), 'yellow' =\u003e array('label' =\u003e __('Yellow', 'twentyfifteen'), 'colors' =\u003e array('#f4ca16', '#ffdf00', '#ffffff', '#111111', '#111111', '#f1f1f1')), 'pink' =\u003e array('label' =\u003e __('Pink', 'twentyfifteen'), 'colors' =\u003e array('#ffe5d1', '#e53b51', '#ffffff', '#352712', '#ffffff', '#f1f1f1')), 'purple' =\u003e array('label' =\u003e __('Purple', 'twentyfifteen'), 'colors' =\u003e array('#674970', '#2e2256', '#ffffff', '#2e2256', '#ffffff', '#f1f1f1')), 'blue' =\u003e array('label' =\u003e __('Blue', 'twentyfifteen'), 'colors' =\u003e array('#e9f2f9', '#55c3dc', '#ffffff', '#22313f', '#ffffff', '#f1f1f1')))"],"doc":{"description":"Filters the color schemes registered for use with Twenty Fifteen. The default schemes include 'default', 'dark', 'yellow', 'pink', 'purple', and 'blue'. Associative array of color schemes data. Associative array of information for setting up the color scheme. Colors are defined in the following order: Main background, sidebar background, box background, main text and link, sidebar text and link, meta box background. } }","long_description":"","tags":[]},"end_line":180,"line":180,"name":"twentyfifteen_color_schemes","type":"filter"}]}},{"arguments":[],"doc":{"description":"Gets the current Twenty Fifteen color scheme.","long_description":"","tags":[{"content":"Twenty Fifteen 1.0","name":"since"},{"content":"An associative array of either the current or default color scheme hex values.","name":"return","types":["array"]}]},"end_line":270,"hooks":[],"line":261,"name":"twentyfifteen_get_color_scheme","namespace":null,"uses":{"functions":[{"end_line":262,"line":262,"name":"get_theme_mod"},{"end_line":263,"line":263,"name":"twentyfifteen_get_color_schemes"},{"end_line":265,"line":265,"name":"array_key_exists"}]}},{"arguments":[],"doc":{"description":"Returns an array of color scheme choices registered for Twenty Fifteen.","long_description":"","tags":[{"content":"Twenty Fifteen 1.0","name":"since"},{"content":"Array of color schemes.","name":"return","types":["array"]}]},"end_line":290,"hooks":[],"line":281,"name":"twentyfifteen_get_color_scheme_choices","namespace":null,"uses":{"functions":[{"end_line":282,"line":282,"name":"twentyfifteen_get_color_schemes"}]}},{"arguments":[{"default":null,"name":"$value","type":""}],"doc":{"description":"Sanitization callback for color schemes.","long_description":"","tags":[{"content":"Twenty Fifteen 1.0","name":"since"},{"content":"Color scheme name value.","name":"param","types":["string"],"variable":"$value"},{"content":"Color scheme name.","name":"return","types":["string"]}]},"end_line":310,"hooks":[],"line":302,"name":"twentyfifteen_sanitize_color_scheme","namespace":null,"uses":{"functions":[{"end_line":303,"line":303,"name":"twentyfifteen_get_color_scheme_choices"},{"end_line":305,"line":305,"name":"array_key_exists"}]}},{"arguments":[],"doc":{"description":"Enqueues front-end CSS for color scheme.","long_description":"","tags":[{"content":"Twenty Fifteen 1.0","name":"since"},{"content":"wp_add_inline_style()","name":"see"}]},"end_line":351,"hooks":[],"line":320,"name":"twentyfifteen_color_scheme_css","namespace":null,"uses":{"functions":[{"end_line":321,"line":321,"name":"get_theme_mod"},{"end_line":328,"line":328,"name":"twentyfifteen_get_color_scheme"},{"end_line":331,"line":331,"name":"twentyfifteen_hex2rgb"},{"end_line":332,"line":332,"name":"twentyfifteen_hex2rgb"},{"end_line":338,"line":338,"name":"vsprintf"},{"end_line":339,"line":339,"name":"vsprintf"},{"end_line":340,"line":340,"name":"vsprintf"},{"end_line":342,"line":342,"name":"vsprintf"},{"end_line":343,"line":343,"name":"vsprintf"},{"end_line":344,"line":344,"name":"vsprintf"},{"end_line":348,"line":348,"name":"twentyfifteen_get_color_scheme_css"},{"end_line":350,"line":350,"name":"wp_add_inline_style"}]}},{"arguments":[],"doc":{"description":"Binds JS listener to make Customizer color_scheme control.","long_description":"\u003cp\u003ePasses color scheme data as colorScheme global.\u003c/p\u003e","tags":[{"content":"Twenty Fifteen 1.0","name":"since"}]},"end_line":364,"hooks":[],"line":361,"name":"twentyfifteen_customize_control_js","namespace":null,"uses":{"functions":[{"end_line":362,"line":362,"name":"wp_enqueue_script"},{"end_line":362,"line":362,"name":"get_template_directory_uri"},{"end_line":363,"line":363,"name":"wp_localize_script"},{"end_line":363,"line":363,"name":"twentyfifteen_get_color_schemes"}]}},{"arguments":[],"doc":{"description":"Binds JS handlers to make the Customizer preview reload changes asynchronously.","long_description":"","tags":[{"content":"Twenty Fifteen 1.0","name":"since"}]},"end_line":374,"hooks":[],"line":372,"name":"twentyfifteen_customize_preview_js","namespace":null,"uses":{"functions":[{"end_line":373,"line":373,"name":"wp_enqueue_script"},{"end_line":373,"line":373,"name":"get_template_directory_uri"}]}},{"arguments":[{"default":null,"name":"$colors","type":""}],"doc":{"description":"Returns CSS for the color schemes.","long_description":"","tags":[{"content":"Twenty Fifteen 1.0","name":"since"},{"content":"Color scheme colors.","name":"param","types":["array"],"variable":"$colors"},{"content":"Color scheme CSS.","name":"return","types":["string"]}]},"end_line":770,"hooks":[],"line":385,"name":"twentyfifteen_get_color_scheme_css","namespace":null,"uses":{"functions":[{"end_line":386,"line":386,"name":"wp_parse_args"}]}},{"arguments":[],"doc":{"description":"Outputs an Underscore template for generating CSS for the color scheme.","long_description":"\u003cp\u003eThe template generates the css dynamically for instant display in the Customizer preview.\u003c/p\u003e","tags":[{"content":"Twenty Fifteen 1.0","name":"since"}]},"end_line":800,"hooks":[],"line":780,"name":"twentyfifteen_color_scheme_css_template","namespace":null,"uses":{"functions":[{"end_line":797,"line":797,"name":"twentyfifteen_get_color_scheme_css"}]}}],"path":"src/wp-content/themes/twentyfifteen/inc/customizer.php","root":"/var/www/html/core","uses":{"functions":[{"end_line":113,"line":113,"name":"add_action"},{"end_line":253,"line":253,"name":"function_exists"},{"end_line":273,"line":273,"name":"function_exists"},{"end_line":293,"line":293,"name":"function_exists"},{"end_line":352,"line":352,"name":"add_action"},{"end_line":365,"line":365,"name":"add_action"},{"end_line":375,"line":375,"name":"add_action"},{"end_line":801,"line":801,"name":"add_action"}]}}
This is worth finishing up and given a proper review, just needs some careful minor changes. I'll see if I can make a start on those.
There's a lot of syntax in core that isn't conveyed in the unit tests here :( This is going to take a while..
There's also a whole bunch of hacky workarounds for parsing bugs in the theme.. since it was too hard to fix the parser..
For example, this PR extracts @type from @param hashes, but it's nested side-by-side rather than nested inside the param.. but the reference expects @type not to be extracted at all: https://github.com/WordPress/wporg-developer/blob/bcb196110099a2cd898230834022b6237917e793/source/wp-content/themes/wporg-developer-2023/inc/formatting.php#L598-L680 plus a bunch of other markup changes in there.
I accidentally pushed some changes to this branch that I didn't intend on (that's why there's a force-push of me undoing that), these are now where I intended them to be, on my fork: https://github.com/WordPress/phpdoc-parser/compare/modernize-parser-php8...dd32:phpdoc-parser:modernize-parser-php8 Some of those can be brought over without issue, but some are just me trying to make the output sane for my development diffs:
npm run wp-env run cli wp parser export ./core/src/wp-includes/html-api/ export.json
diff -U10 <(jq -S . ../phpdoc-parser-pr248/export.json) <(jq -S . export.json) | colordiff
These changes were generated primarily by an AI coding assistant. I have yet to manually review the changes thoroughly.
@johnbillion is this still the case, or are you done with correcting AI! Happy to also give this a review
This is still the case. It needs a full review.
One high-level note, the original structure of the code strongly reflects (no pun intended) phpdocumentor’s - e.g. the File_Reflector class extends phpDocumentor\Reflection\FileReflector, and most of the other reflector classes extend phpDocumentor\Reflection\BaseReflector.
This PR retains that structure, which is fair enough. However, it also means shoehorning PHPStan\PhpDocParser to fit in with what is now kind of a legacy structure. I think that this is particularly visible in the File_Reflector class, which serves as an entry point and now does a lot of the heavy lifting (i.e. parsing at the top level; lots of helper methods such as processXYZ). It’s possible that PHPStan\PhpDocParser might offer a more idiomatic way to do these things, and that the same result could be achieved with “thinner” wrappers around its classes.
(I apologize for the drive-by comment and lofty words. I’m aware that the code here was initially AI-generated, and that AI has an easier time generating code that fits into an existing structure compared to “re-thinking” that structure. However, the ensuing verbosity of the code might’ve played a role in this PR not receiving a lot of reviews yet. I’d also like to acknowledge that a verbose solution is better than none 🙂 and that I probably won’t be able to offer an alternative myself any time soon. If anything, maybe the above suggestions can be used to guide an LLM to a solution with a lower LOC count.)
Edit: The DeepWiki docs (which I realize are also AI-generated) seem quite helpful in understanding the overall architecture of PHPStan\PhpDocParser. (Assuming they're correct 😅)
Good observation. It definitely felt like getting Claude to reproduce the existing structure with a new parser was going to be simpler than rearchitecting the approach.
These changes are still building on top of legacy code such as Posts 2 Posts and its output needs to work with the developer.wordpress.org theme unless you want to also rewrite that at the same time. I think that means that the best we can do is add good test coverage for whatever format is required of developer.wordpress.org and ensure they pass. The wrappers around phpstan/phpdoc-parser and nikic/php-parser and their resulting data structure remain necessary unless we can start from scratch, which is even more impractical than what we're doing here.
@dd32 I have merged your commits into this branch, made some tweaks, and updated the tests. How can we determine what's left to do?
I've managed to get a working version of both the original docparser and the updated version (from this PR) so I can run exports to compare the json files. Next step is to review the import process to determine what fields are important, and then compare that to the export. At the same time I'm leaving comments on the PR around onboarding documentation for contributors. I will pick up testing in the new year after I return from my year end leave.
After some digging, I do not really understand the decision to drop phpDocumentor. The latest version of the phpDocumentor docblock parser even uses the phpstan phpdoc-parser internally for type parsing. Even phpstubs/wordpress-stubs is using phpDocumentor to create phpstan compatible array shape type docs from the legacy WordPress params. Meanwhile, out of the box, the phpstan docblock-parser does really strange things to the legacy WordPress array shape docs.
Seems to me that phpDocumentor provides better backwards compatibility and arguably an easier upgrade path
Seems to me that phpDocumentor provides better backwards compatibility and arguably an easier upgrade path
I was wrong.
While I got pretty close to achieving the same output as the old parser, getting to 100% identical output (with some whitespace and type related issues excluded) seems to stay frustratingly out of reach.
Most of the issues are caused by phpDocumentor not considering any nodes that are not the direct child of a function or class method or at the root of the file, and some issues with type name resolution. Since the API of the reflection library is very restrictive and locked down by design, workarounds for these issues are simply not possible or require ugly hacks or lots of duplicated code.
It might still be a good idea to use the phpdocumentor/reflection-docblock library for docblock parsing, because it handles the WordPress-style array shape param tags better than the phpdoc docblock parser alone
https://github.com/WordPress/phpdoc-parser/pull/251 is a less ambitious approach that focuses on:
- php-parser support for more modern syntax
- minimal package upgrades
- minimal code changes
It's working to generate docs and may be an easier review and more incremental change.