Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Stop advocating the static API for the CssSelector component #15850

Closed
@stof

Description

@stof

The usage of a static API in the CssSelector component creates many issues for us, because we have a global state maintained for it (currently only whether the HTML extension is enabled, but #12852 is asking for more state too), and changes to this global state will impact all other code using the component.
The Translator object graph (which is the internal non-static API of the component) also gets instantiated again and again for each converted selector, which might not be optimal.

The static states causes us issues, as it forbids implementing #8404 for instance.

My proposal is to implement class having a similar API but in a non-static way (I already suggested in a discussion somewhere, but I cannot find it anymore).

interface SelectorConverterInterface
{
    /**
     * Translates a CSS selector to an XPath expression.
     *
     * @param string $cssExpr
     * @param string $prefix
     *
     * @return string
     */
    public function cssToXPath($cssExpr, $prefix = 'descendant-or-self::');
}

One of the possibilities is to use the existing Translator class for that, but the TranslatorInterface contains an additional method compared to the proposed interface (the selectorToXPath method does not make sense in the interface btw. It is an internal method IMO).
The translator instantiation is also not very user-friendly:

  • you have to register the HtmlExtension yourselves if you want to work in an HTML content
  • shortcut parsers (which are performance optimizations for simple common selectors) are not registered by default.

To solve the instantiation part, we could have a factory method in CssSelector allowing to build the necessary class, and getting an argument to decide whether the HTML extension should be enabled or no.
Regarding the interface, this would still need to be discussed whether the existing one is used (deprecating the useless method) or whether a new one is introduced (and then whether we wrap the Translator or implement the interface on it directly). If we create another class implementing the interface, it will also solve the instantiation issue.
It might make sense to keep the Translator internal though and create a new class for the public API of the component. This would decouple totally the public API of the component from the way it is implemented internally (we already rewrote the internal implementation in the past btw; 2.0 was not using this Translator thing). Thus, the name Translator is probably not the best name for the public API of the component.

A nice benefit of the non-static API is that it would also be possible to build other implementation of the interface, for instance caching implementations (to avoid converting the same selector again and again).

Once this is available, the DomCrawler component could remember whether it was created for an HTML or XML content (this requires #15849 first as it is currently possible to be created for both) and create its own instance of the css selector converter (only when needed of course), which would not be affected by global state changes.

What do you think about it ?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions