cancel
Showing results for 
Search instead for 
Did you mean: 

Virtual Types Naming Convention

vkublytskyi
Adobe Team

There are only two hard things in Computer Science: cache invalidation and naming things.

 Phil Karlton

 

Magento Dependency Injection implementation comes with the very powerful concept of Virtual Types.  However, with great power comes great responsibility. Virtual types provide a declarative way to create multiple instances of a single PHP class, each with a different constructor argument. This allows developers to implement new functionality or change Magento behavior with less code and so with fewer bugs. As with any other subject in programming, a good naming of virtual types allows writing code that is easy to understand and support. In this post we describe the future naming scheme that Magento will be using from now on for naming of virtual types. Feedback is welcome!

 

Magento merges all di.xml files for an area so all virtual types are available from all modules. Therefore, a virtual type declared in one module may be used or even reconfigured in another. That's great! But what if two modules create virtual types with the same name? A name collision occurs and a single virtual type will be created instead of two separate instances. Worse yet, such an inapplicable Dependency Injection configuration may not trigger any error or exception and lead to hard-to-detect bugs.

 

To avoid naming conflicts, all virtual type names should:

  • be in PHP FQCN (Full Qualified Class Name) format without leading backslash (e.g. Magento\Catalog\Pricing\Price\Pool)
  • use Vendor\Module\ as a root namespace (equal to the PHP root namespace of the module where the virtual type is introduced)

To name a virtual type, just answer the question "If there is no suitable ready-to-use implementation, what PHP class would I have to create and how would it be named?"

 

Magento already uses the above naming convention in some places but there are a lot of virtual type names that use camel case. Does that mean that all of them will be renamed in upcoming Magento release? No! Existing virtual type names will not be changed for backward compatibility reasons. Only new virtual type names from now on will follow this rule.

 

We chose FQCN-like virtual type names because we tried to satisfy additional criteria that are significant to us:

  • virtual type names should be consistent with other names in Magento
  • naming should help provide backward compatibility
  • naming should not expose implementation details (when some object injects a dependency it should not matter whether it is a virtual type or real PHP class)
  • naming should help a developer discover code (where to search for injected dependency: in di.xml or in PHP code?)
  • avoid conflicts of virtual types and PHP classes
  • naming should allow introduction of nested scopes

 

FQCN-like names have two flaws. First, naming conflicts between virtual types and PHP classes are possible. This issue is not critical because virtual types and PHP classes share the same Vendor\Module\ namespace, so it is easy for the module developer to control names.

 

Second, FQCN-like naming scheme does not readily allow differentiation of virtual types from PHP classes. This is a subject of serious concern. If you are using the PhpStorm Magento 2 plugin, it allows developers to navigate to virtual type declarations or to PHP class source code. Nevertheless, Magento does not want to impose any IDE or other tools. Developers should work with an environment that is comfortable for them.

 

An alternative naming approach that we considered was Vendor_Module::virtualTypeName. This naming scheme explicitly distinguishes between PHP classes and virtual types. But it also has an important drawback as it creates difficulties with respect to supporting backward compatibility.

 

In conclusion, this post has described the planned new naming convention for virtual types. This approach avoids virtual type name conflicts and allows Magento code to evolve without breaking backward compatibility. We always welcome community feedback, so please leave any comments below either in support of the proposal, or sharing your opinions on a better approach.

18 Comments
fschmengler
M2 Certified

I would like to challenge this decision. You are making the right observations but to be frank I don't understand your conclusion.

 

Let's go through your criteria:

 

  • virtual type names should be consistent with other names in Magento

Makes sense, but which names? Since virtual types are a pure XML configuration thing, why should they be named like PHP classes? The naming could be consistent with other unique things like observer names and indexer ids (module_virtual_type_name) or if that's a M1 relict, ACL resources and templates (Vendor_Module::virtual_type_name)

 

  • naming should help provide backward compatibility

If "we already started doing it it this way and don't want to change it" is a strong argument, this will limit moving forward Magento 2 in many aspects. So let's be open for change if it's an improvement. Backwards compatibility is not even that hard in this case: keep the old virtual types and add the same with the new naming convention next to it (can you mark XML as deprecated?)

 

  • naming should not expose implementation details (when some object injects a dependency it should not matter whether it is a virtual type or real PHP class)

This contradicts the next point...

 

  • naming should help a developer discover code (where to search for injected dependency: in di.xml or in PHP code?)

... which should be high priority for better developer experience IMHO.

 

You say "If you are using the PhpStorm Magento 2 plugin, it allows developers to navigate to virtual type declarations or to PHP class source code. Nevertheless, Magento does not want to impose any IDE or other tools. Developers should work with an environment that is comfortable for them."

 

For me, PhpStorm and the plugin are a must have, so I'm not even feeling imposed. But if any code requires non-standard tools to even understand and navigate it effectively, this is a very bad sign. In this case it is even without good cause and could easily be avoided.

 

I can only imagine one reason to want virtual types to be transparent (i.e. looking like real types): the ability to exchange them with a real class as needed. But if you want to do this, you will need to change the definition in di.xml anyways, so it's not making anything significantly easier. Please let me know if I'm missing something.

 

  • avoid conflicts of virtual types and PHP classes

Easy: Don't name them like PHP classes ;-)

 

  • naming should allow introduction of nested scopes

This is something I don't really understand. Do you mean namespaces? There are already too many nested namespaces in Magento, but "sub_sub_sub"  works just as well as "Sub\Sub\Sub". To be fair, you did not mention this as argument for one or the other solution.

 

In conclusion, I would like to know more about your reasons not to use Vendor_Module::virtualTypeName, which would be a perfect solution IMHO.

 

Vinai
Magento Master

VirtualType names that could be real PHP class names and don't indicate they are virtual typeshave already cost me hours of precious lifetime.  

Why make debugging M2 harder than it has to be? The post doesn't even explain the reasoning beyond "...it creates difficulties with respect to supporting backward compatibility." How so? I'm not convinced.

 

I ask all developers to add the word "Virtual" to theirs VirtualType class names or alternatively to use a string that can't be a real PHP class name to make it easy to read and debug the configuration.

(Side note regarding PHPStorm: I meet many developers in person and online who use a different IDE or editor.)

kalenjordan
M1 Certified

As someone whose eyes are pretty fresh to M2 virtual types (read: I have no idea what this is about), it might be useful to see an quick example feature implementation that shows the utility of the virtual types naming scheme that you're discussing.  Might help to take it out of the abstract into the practical a bit.

Ryan Hoerr
M2 Certified

I agree with most of Fabian and Vinai's feedback. It seems to me it would avoid confusion to advocate something immediately identifiable as a virtual type.

 

My preference would be a 'Virtual' keyword, like Magento\Catalog\Pricing\Price\VirtualPool. The Vendor_Module::virtualTypeName pattern feels too arbitrary (granted, it is arbitrary--but still, more likely to result in questioning 'but why?').

 

Kalen, here are a couple examples from core. Note the format of 'name' in each. Before the convention:

<virtualType name="BraintreeVaultPaymentConfig" type="Magento\Payment\Gateway\Config\Config">
    <arguments>
        <argument name="methodCode" xsi:type="const">Magento\Braintree\Model\Ui\ConfigProvider::CC_VAULT_CODE</argument>
    </arguments>
</virtualType>

And using it:

<virtualType name="Magento\Paypal\Model\Payflow\ProFactory" type="Magento\Paypal\Model\ProFactory">
    <arguments>
        <argument name="instanceName" xsi:type="string">Magento\Paypal\Model\Payflow\Pro</argument>
    </arguments>
</virtualType>

 

vkublytskyi
Adobe Team

Hi @fschmengler,

Thank you for so detailed comment. I will try to answer all of your questions and explain why Vendor_Module::virtualTypeName naming scheme was rejected. Sorry if I miss something.

 

  • virtual type names should be consistent with other names in Magento

Makes sense, but which names? Since virtual types are a pure XML configuration thing, why should they be named like PHP classes?

We place consistency as the criteria because we don't wont introduce any new format to XML files (e.g. $virtualTypeName or vendor|module|virtualTypeName).

Virtual types are declared in di.xml files and they have the same importance and may be used in the same places as PHP classes and interfaces. Observer and plugin names have a different nature. They are registration keys that are used in a very limited context. Template names are similar and this is the reason why we considered Vendor_Module::virtualTypeName naming scheme.

Let's see the code example:

 

<type name="Vendor\Model\Foo"><!-- real PHP class -->
  <arguments>
    <argument name="arg1" xsi:type="object">Vendor\Module\Bar</argument><!-- real PHP class -->
    <argument name="arg2" xsi:type="object">Vendor_Module::baz</argument><!-- virtual type -->
</arguments> </type>

In this code snippet Vendor\Module\Bar and Vendor_Module::baz have equal meaning but have the different naming format. FQCN-like virtual type names allow having consistent names for all "injectable" that exists in di.xml files:

<type name="Vendor\Model\Foo"><!-- real PHP class -->
  <arguments>
    <argument name="arg1" xsi:type="object">Vendor\Module\Bar</argument><!-- real PHP class -->
    <argument name="arg2" xsi:type="object">Vendor\Module\Baz</argument><!-- virtual type -->
</arguments> </type>
  • naming should help provide backward compatibility

If "we already started doing it it this way and don't want to change it" is a strong argument, this will limit moving forward Magento 2 in many aspects. So let's be open for change if it's an improvement. Backwards compatibility is not even that hard in this case: keep the old virtual types and add the same with the new naming convention next to it (can you mark XML as deprecated?)

We are willing to improve. Changing something is not a blocker for an improvement. However, we should compare a cost of change and support in future and improvement value. I will describe Vendor_Module::virtualTypeName naming scheme drawback in a context of backward compatibility below in this comment.

XML element may be marked as deprecated by the comment:

 

<!--
@deprecated reason of depreciation
@see link to recommended approach
-->

 

 

  • naming should not expose implementation details (when some object injects a dependency it should not matter whether it is a virtual type or real PHP class)

This contradicts the next point...

  • naming should help a developer discover code (where to search for injected dependency: in di.xml or in PHP code?)

Exactly! As often happens in software development this is not a question about selecting single right decision but about tradeoffs.

 

... which should be high priority for better developer experience IMHO.

Developers experience indeed is a high priority goal. But developers experience is not ended after debugging existing Magento code and adding own functionality. It only begins at this point! A custom solution should be supported and evolved in future. Read more why FQCN-like names are better (at least we think so now) then Vendor_Module::virtualTypeName for long time support in next item.

 

I can only imagine one reason to want virtual types to be transparent (i.e. looking like real types): the ability to exchange them with a real class as needed.

Yes! FQCN-like names allow as replace a virtual type with a real class without a necessity to modify code where this instance is injected to other virtual type or real class. It also allows replacing a deprecated class with a new virtual type. Such refactoring provide the possibility to iteratively replace inheritance with aggregation. Even if some refactoring is not possible now because of current virtual types implementation we may change it without breaking backward compatibility. 

 

With Vendor_Module::virtualTypeName for backward compatibility it is better to not remove virtual type but change value of type attribute. That's not hard. But this also means that we are not able to improve developer experience as when a developer will find injecting of virtual type instead of looking for a class definition she will search for virtual type first. So, in a future, we may fall in the situation when there are a lot of virtual types that are really don't needed.

 

  • naming should allow introduction of nested scopes

This is something I don't really understand. Do you mean namespaces?

Yes, we assume namespaces here. Namespaces help to provide logical structure so are useful for code expressiveness. Namespaces may be achieved by any string separator but we have requirement of consistency for a naming format that limits variants.

vkublytskyi
Adobe Team

Hi @Vinai,

 

We are really worried about issues that you described. We share your opinion that writing code for Magento should be possible and convenient even in a simple text editor.

Would you describe more detailed what exactly is hard during debugging when names of virtual types look like real PHP classes names?

 

For example, we are exploring some implementation and see code that references to some instance:

 

<!-- in di.xml -->
<type name="Vendor\Module\Foo">
  <arguments>
     <argument name="arg" xsi:type="object">Vendor\Module\Bar</argument>
  </arguments>
</type>
<!-- or even in page configuration xml -->
<block class="Vendor\Module\Bar" />

How should we understand what  Vendor\Module\Bar is and how it behave? If we have the strict rule for virtual type names, then we should look for PHP file at the strictly defined path (according to PSR-4 <VendorDir>/<ModuleDir>/Bar.php). If the file is not present then this is a virtual type.

But should we really start from this step? Even if this is a real class it may have some complex configuration in di.xml file (it may be configured with not default dependencies and pluginized). So to understand how instance will behave it is always better for searching it through all di.xml files in a project at the beginning.

 

@Vinai, is difficult of debugging really caused by FQCN-like names or the root cause of an issue is the lack of information what virtual types are and how they may be used across Magento configuration files?

vkublytskyi
Adobe Team

Hi @kalenjordan,

 

Unfortunately, we do not have a detailed description of virtual types at our DevDocs now. You may help us by listing questions what you have about virtual types.

 

Our development documentation is powered by GitHub so any community member may improve it and provide missed documentation. Any help is welcome.

Vinai
Magento Master

Hi @vkublytskyi,

thanks for your answer.

Regarding virtual types per se, I feel quite comfortable with them and have used them quite a bit already. In some situations they are useful.

 

Gathering the configuration of a given object from all di.xml files is one of the most tedious tasks of developing on the Magento 2 platform. Hiding the fact that something is a virtual type just adds complexity that is unnecessary in my eyes.

Writing virtual types is easy and it's actually fun building nested structures.

But reading and understanding the reference structure someone else has built (or that I built a month ago) is hard.

 

Usually I'm reading through di.xml files when I'm looking for how the constructor arguments for a specifc class are configured.

When I'm trying to build a picture of how things are wired together it is valuable to know if a given class is a virtual type or a real class.

 

When I see a node `<argument name="somthing" xsi:type="object">Vendor/Foo/Bar</argument>` then I don't want to interrupt my task of understanding the class I'm currently looking into by checking if `/Vendor/Foo/Bar` is a virtual type or not. I want to be able to gather as much information about `/Vendor/Foo/Bar` as possible just from the string `/Vendor/Foo/Bar`, without having to look any further. If I have to check if `/Vendor/Foo/Bar` is a virtual type or a real PHP class it interrupts my flow and forces me to open a new scope in my brain, potentially loosing the thread I'm currently following, forcing me to backtrack and start again.

 

Regarding backward compatibility, a scenario where someone wants to replace a virtual type with a real class probably introduces other changes too. Each virtual type is mapped to a real class anyway, I don't see a real issue of just mapping the virtual type to a differnt real class.

Personally I don't care about strict rules for virtual type names. As long as it includes the vendor and module name, and indicates it is a virtual type, I'm happy if every module uses it's own naming convention.

 

 

 

vkublytskyi
Adobe Team

Hi @Ryan Hoerr,

Thanks for your feedback and provided examples of virtual types usage.

 

One small note about code example that uses FQCN-like virtual type name. This code was written before agreement on the naming convention. Fact that FQCN-like virtual type names are already in use was the reason to approve it.

 

We also considered similar naming convention as you described but with "Virtual" as a suffix (e.g. Vendor\Module\FooVirtual) to be more consistent with PHP interfaces naming. But we did not find strong enough arguments to prefer it.

vkublytskyi
Adobe Team

@Vinai, thank you for the explanation. We assumed that when a developer explores configuration and see `<argument name="somthing" xsi:type="object">Vendor/Foo/Bar</argument>` it is more important what injected object supposed to do (and this should be reflected in virtual type name or in class name) than how it is implemented (as real PHP class, virtual type or interface). But different people work with code in a different way and think about code in a different way. That's why any feedback is very important. 

Ben Marks
Certification Board Member

A tip of the hat to @fschmengler for starting the conversation!

fschmengler
M2 Certified

Thank you @vkublytskyi for your detailed response, I finally get around to answer, although much has been said by the others already.

 

> In this code snippet Vendor\Module\Bar and Vendor_Module::baz have equal meaning but have the different naming format. FQCN-like virtual type names allow having consistent names for all "injectable" that exists in di.xml files:

 

While I understand that, I still think that the goal to make virtual types distinguishable should be more important. Be it with a "Virtual\" prefix as Vinai suggested, a "Virtual" suffix or even somewhere in the middle:

  • Virtual\Vendor\Module\Bar
  • Vendor\Module\BarVirtual
  • Vendor\Module\VirtualBar

I don't particularly like any of these but they serve the goals and I could live well with it as a compromise.

 

> Yes! FQCN-like names allow as replace a virtual type with a real class without a necessity to modify code where this instance is injected to other virtual type or real class. It also allows replacing a deprecated class with a new virtual type. Such refactoring provide the possibility to iteratively replace inheritance with aggregation. Even if some refactoring is not possible now because of current virtual types implementation we may change it without breaking backward compatibility.

 

"It also allows replacing a deprecated class with a new virtual type" - That's something I did not think about, but the use case where this actually works is very limited. The deprecated class has to implement an interface which is not deprecated, because if the client classes have the class itself as a dependency you will have to change the code anyways as virtual types cannot be used in the code. And we only have an advantage if the deprecated class is defined in several places as local preference. If it's a global preference, there's only one place where we need to replace the deprecated class with the virtual type. So I think the benefit in reality is not as big as it might seem.

"Even if some refactoring is not possible now because of current virtual types implementation we may change it without breaking backward compatibility" - I'd like to hear more about that. Will virtual types become real classes via code generation? That would change a lot!

 

vkublytskyi
Adobe Team

@fschmengler, during an internal discussion, we did not find strong enough arguments why it is important for a developer to distinguish virtual types from PHP classes to put the highest priority for this criteria. We investing a lot of effort to make Magento code more declarative than imperative. Code, especially XML configuration files, should describe "what" is done instead of "how" it is done.

Please explain reasons that you see why it is important for developers to distinguish virtual types from PHP classes. How will it help them to understand the system if, even with PHP classes, developers still need to look at DI and plugins configuration?

 

Regarding replacing of deprecated classes with virtual types. Magento still has places where extending from abstract classes is required. Inheritance is not recommended in Magento 2 and composition should be preferred. Possibility to replace PHP class with a virtual type allow to perform refactoring and replace inheritance with a composition which in future allow eliminating layer supertypes.

 

> Will virtual types become real classes via code generation?

We do not plan this now. But we also do not want to take decisions that would limit us in future.

Vinai
Magento Master

@vkublytskyiIn theory encapsulation of the "how" is great.

In practice Magento 2 isn't there yet.

Most of the time, when I work on Magento 2 modules, I unfortunately need to know the "how", that is, the implementation details of the core.

The functionality provided by the interfaces is just not good enough, or poorly named, or the implementation is does something different then expected.

Examples? Please tell me how to add get the current customers or visitors cart using only methods provided by the service contract. Same for product gallery images. There are many more. In every module I have developed so far I run into such obvious and legit use cases, but it is not covered by the official API. In every such case I need to dig into the implementation details of the core framework and modules to figure out the most stable way to do the required task. This by far is the biggest block of time during Magento 2 development.

It is quite frustrating trying to develop on Magento 2 in the way it's recommended, but failing every time due to plattform limitations. So far there hasn't been a single module I've written I was able to implment using only service contracts.

 

From my point of view, in order to be develop efficiently I for Magento 2, I need to know the internals of the plattform.

I hope that will change in future, but currently it certainly isn't there yet.

For that reason it saves me valuable hours to see quickly how something is implemented. That includes virtual types.

Insisting on keeping that hidden just adds to my frustration. Please help us developers be more effective!

 

Ben Marks
Certification Board Member

@vkublytskyi I think that Vinai has stated it best: 

 


@Vinai wrote:

If I have to check if `/Vendor/Foo/Bar` is a virtual type or a real PHP class it interrupts my flow and forces me to open a new scope in my brain, potentially loosing the thread I'm currently following, forcing me to backtrack and start again.

VirtualType names that could be real PHP class names and don't indicate they are virtual typeshave already cost me hours of precious lifetime.

This seems like the kind of thing that we could implement as part of a BC-breaking release. But I think it may also be confusing and would add difficulty for not so much benefit? I wonder also if we or the community may work on some tools to readily surface virtuals from literals. 

 

@Vinai Max & I are working towards a community-visible backlog, and I think it should include missing coverage in service layer. I think this can be prioritized like issues. Thoughts?

 

Vinai
Magento Master

Maybe a new thread in the feature request forum, or a label that can be addd to topics there?

dmaciej
M1 Certified

I read the whole discussion and I'm glad Magento Team wants to discuss this kind of issues at the public.
Personally, I understand possible disadvantages related to the idea of creating virtual types named like virtualTypeAlias but, on the other hand, I think we should separate them to make it easier to recognition by developers.he o
In my opinion, we should compromise and use Virtual\Vendor\Module\Bar.

bkubicki
M2 Certified

In my opinion, even if `virtual types` are sometimes really useful, they are hard do to debug for developers starting their adventure with Magento 2. Using it recently I was wondering if it would be useful for us to generate `virtual types` classes as it is done with proxies. Then they can be easily explored. Maybe it breaks a little concept of `virtual type` as they are not that much virtual, but on the other hand, they wouldn't be created by developer, but generate automaticaly during `di:compile`.