Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Failover methods in ViewableData are not cleared correctly when a new failover record is set #11574

Open
GuySartorelli opened this issue Jan 22, 2025 · 1 comment

Comments

@GuySartorelli
Copy link
Member

GuySartorelli commented Jan 22, 2025

Found in CMS 5 - presumably also affects CMS 6

The SilverStripe\Core\Extensible trait along with the SilverStripe\Core\CustomMethods trait are responsible for allowing methods from extensions to be called on classes which have those extensions applied.
ViewableData has a concept of a failover which allows one object to call methods from another object (e.g. call methods on a controller, and it will "failover" to calling methods on the data record the controller represents).

These two things combine to create the following bug:

All methods available on a page are defined as "failover" methods on the page's controller. If a page class shares the same controller as another page class (e.g. two page classes both defaulting to use PageController), and Page A has methods which Page B doesn't have, and Page B is a child of Page A - the hasMethod() method will return true for methods from Page A even when hasMethod() is called on Page B's controller.

This seems to be caused by the $extra_methods array being held statically (which is done for performance reasons) and not correctly clearing/removing methods out of that array when setting or removing a failover from a class.

Related

Reproduction steps

Note that for steps 1 and 2 you can use the BasicElementalPage class provided by frameworktest.

  1. Create a new page class that extends Page. DO NOT give it its own controller class (i.e. it will fall back to using PageController)
  2. Add some methods to that new page class via an extension (in my case using ElementalPageExtension)
  3. In the CMS, create a new record of that page type and publish it.
  4. In the CMS, create a new child of that page. The child should be a regular Page record. Publish it.
  5. In your page template (either templates/Page.ss or templates/Layout/Page.ss) call the method that exists on your new page type (in my case using the $ElementalArea template variable)
  6. Try to view the child page in the front-end.

Note that viewing a Page record at the root level will not throw the exception. Only when the page is a child of the new class will it throw the exception.

@GuySartorelli
Copy link
Member Author

The workaround, at least in-so-far as it affects pages, is to ensure your Page subclasses each have a corresponding PageController subclass even if there's nothing in there beyond the class declaration.

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

No branches or pull requests

1 participant