Skip to content

REST API: Remove a redundant private property in WP_REST_Template_Autosaves_Controller. - #13041

Open
CallumBW95 wants to merge 1 commit into
WordPress:trunkfrom
CallumBW95:phpstan/unused-property-template-autosaves-controller
Open

REST API: Remove a redundant private property in WP_REST_Template_Autosaves_Controller.#13041
CallumBW95 wants to merge 1 commit into
WordPress:trunkfrom
CallumBW95:phpstan/unused-property-template-autosaves-controller

Conversation

@CallumBW95

Copy link
Copy Markdown

WP_REST_Template_Autosaves_Controller declares its own private $parent_post_type and assigns it in the constructor on the line after parent::__construct(), which already performs that assignment:

public function __construct( $parent_post_type ) {
	parent::__construct( $parent_post_type );
	$this->parent_post_type = $parent_post_type;   // never read

WP_REST_Autosaves_Controller declares $parent_post_type privately as well, so PHP allocates a separate slot for each declaration rather than reusing one. Nothing on the subclass ever reads its copy, which is the single property.onlyWritten occurrence in the baseline:

Property WP_REST_Template_Autosaves_Controller::$parent_post_type is never read, only written.
  src/wp-includes/rest-api/endpoints/class-wp-rest-template-autosaves-controller.php

Removing the declaration and the assignment leaves the two remaining assignments in that block over-aligned, so they are realigned to match.

Why this cannot change behaviour

Private properties with the same name in a class and its subclass do not share storage, and a method inherited from the ancestor always reads the ancestor's slot regardless of whether the subclass declares its own. That is worth demonstrating rather than asserting:

class A { private $p; public function __construct( $v ) { $this->p = $v; } public function readIt() { return $this->p; } }
class B extends A { private $p; public function __construct( $v ) { parent::__construct( $v ); $this->p = $v; } }
class C extends A {}

var_dump( array_keys( (array) new B( 'val' ) ) );  // ["\0A\0p", "\0B\0p"]  two slots
var_dump( array_keys( (array) new C( 'val' ) ) );  // ["\0A\0p"]            one slot
var_dump( ( new B( 'val' ) )->readIt() );          // 'val'
var_dump( ( new C( 'val' ) )->readIt() );          // 'val'

B is the current shape and C is the shape after this change. The inherited readIt() returns the same value either way, because it reads A's slot in both cases. The subclass copy is pure overhead.

The property is private, so it has no public or extender-facing surface and removing it carries no backward compatibility cost, despite the @since 6.4.0 on its docblock.

How it got there

This class and WP_REST_Template_Revisions_Controller were added together in [56819] (2023-10-10), which introduced both files in one go at 276 and 297 lines. The two constructors are near-copies of one another.

The revisions sibling genuinely needs its shadowed copy. WP_REST_Template_Revisions_Controller::get_parent() reads $this->parent_post_type from the subclass, and the ancestor's copy is private and therefore out of scope there, so without the redeclaration that read would fail:

protected function get_parent( $parent_template_id ) {
	$template = get_block_template( $parent_template_id, $this->parent_post_type );

No method on the autosaves controller does anything equivalent. The property is load-bearing in one of the pair and vestigial in the other, which is consistent with the two constructors having been written from the same starting point. Neither the declaration nor the assignment has been touched since [56819].

One thing I left alone

WP_REST_Autosaves_Controller::$parent_post_type, the ancestor's own copy, also looks like it is written and never read. Its own methods do not reference it, and the reads at WP_REST_Revisions_Controller lines 164 and 826 resolve to that class's slot rather than this one.

PHPStan does not report it, including when the baselines are suppressed during regeneration, so I have not touched it. I cannot account for why the two are treated differently and did not want to act on a claim I could not substantiate. Flagging it in case it is of interest.

Testing instructions

  1. On trunk, npm run typecheck:php reports [OK] No errors, because the occurrence is baselined.
  2. Delete tests/phpstan/baselines/property.onlyWritten.neon and its includes entry, then run it again. PHPStan reports Property WP_REST_Template_Autosaves_Controller::$parent_post_type is never read, only written.
  3. With this branch applied, npm run typecheck:php reports [OK] No errors with the baseline gone and nothing new elsewhere. The baseline directory goes from 70 files to 69.
  4. Regenerating confirms the baseline is genuinely empty rather than hand-removed:
    composer phpstan:baselines -- --identifier=property.onlyWritten
    
    It reports no remaining errors and leaves both deletions in place.
  5. composer lint is clean on the changed file, including the realigned assignments.
  6. Autosave and template coverage passes: npm run test:php -- --filter 'Autosave|Template' gives 779 tests, 2594 assertions, 5 skipped, exit 0. tests/phpunit/tests/rest-api/wpRestTemplateAutosavesController.php was added alongside the class in [56819] and has 23 test methods, all of which construct the controller.
  7. Full suite passes: 30854 tests, 4559519 assertions, 86 warnings, 44 skipped, exit 0, no failures or errors.

Trac ticket: https://core.trac.wordpress.org/ticket/65817

Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5
Used for: tracing the property through the four-class hierarchy to establish which copies are read, git log archaeology identifying [56819] as the origin, constructing the private-shadowing demonstration above, and drafting this description. The diagnosis, the decision to leave the ancestor's copy alone, the baseline regeneration, and verification against full PHPStan, PHPCS and PHPUnit runs were reviewed and confirmed by me in a local development environment.

…osaves_Controller.

`WP_REST_Template_Autosaves_Controller` declared its own private
`$parent_post_type` and assigned it in the constructor immediately after
calling `parent::__construct()`, which already performs the same assignment.
Because both declarations are private, PHP allocates a separate slot for
each, and the subclass copy was never read by any method.

The sibling `WP_REST_Template_Revisions_Controller`, added in the same
changeset, needs its copy: `get_parent()` reads `$this->parent_post_type`
from the subclass, where the ancestor's private property is out of scope.
No method on the autosaves controller does the same, so the property and
its assignment are removed and the surrounding alignment restored.

This was the only `property.onlyWritten` occurrence, so the baseline is
emptied. The file is removed along with its `includes` entry in
`phpstan.neon.dist`.

Props CallumBW95.
See #65817.
@github-actions

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props callumbw95.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@github-actions

Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant