Skip to content

Commit a207012

Browse files
committed
Validate PHP class-names in di.xml files via schema
The preferenceType @for/@type attributes, the typeType @name attribute, the virtualTypeType @type attribute and the pluginType @type attribute contain class-names (FQCNs) which should not start with a leading backslash (U+005C "\") and should not contain other invalid character sequences that would represent an invalid PHP class-name. Previously this was possible and these errors got unnoticed within di.xml configuration file validation. The ObjectManager - a user of these configurations - handles this common error in user input in part so far by removing any trailing slashes with multiple calls like: $type = ltrim($type, '\\'); This change has been introduced in 33ebc24 and could be classified as a workaround for a quite common mistake when specifying an FQCN that despite the varying notations in the PHP manual due to the contextual resolution rules (and different to a file-systems absolute path in POSIX) must not start with a leading separator as type or class-name. When using a string-value as class-name (e.g. class_exists() calls) the class name is always an FQCN as namespacing in PHP is contextual within source-code for identifiers only and not for strings. So relative class-names (like those with a leading backslash) do not apply for strings. This is the case in configuration files like di.xml files. The di.xml files use the urn:magento:framework:ObjectManager/etc/config.xsd schema location which is resolved by UrnResolver (6379732) to lib/internal/Magento/Framework/ObjectManager/etc/config.xsd That schema did validate class-name attribute values only against the simple type of being strings (xs:string). As a class-name in PHP is a valid string also if starting with the backslash character (and other invalid names, like digits in front), this commit extends the schema to validate against the regular expression provided by the PHP manual [1]: ^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$ by adding a new simple-type called "phpClassName" that qualifies the string- base-type with the from PHP manual translated pattern: [a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]* extended for namespaced class-names: ([a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*)(\\[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*)* The change ensures that the said attribute values are validated and invalid class-names are recognized during schema based validation. This change verifies that configured PHP-types can be autoloaded when used w/o smudging (see the ltrim() operation). It has been documented [2] that the leading backslash prevents correct file-resolution when auto- loading with the Composer autoloader, a component actively used by the Magento application. This change adheres to existing PR #8638 and relates to issue #8635. Additionally this commit adds tests for the new phpClassName xsd type with config xml material. Refs: - #8635 - #8638 - [1] https://php.net/language.oop5.basic - [2] http://magento.stackexchange.com/a/161010 - 33ebc24 - 6379732
1 parent 0b243b8 commit a207012

File tree

3 files changed

+85
-6
lines changed

3 files changed

+85
-6
lines changed

lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/_files/invalidConfigXmlArray.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,4 +172,69 @@
172172
"\nLine: 6\n"
173173
],
174174
],
175+
'virtualtype with empty_name' => [
176+
'<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
177+
<virtualType name="" type="TypeName" shared="true"/>
178+
</config>',
179+
[
180+
"Element 'virtualType', attribute 'name': [facet 'pattern'] The value '' is not accepted by the pattern '" .
181+
"([a-zA-Z_\x7f-\xc3\xbf][a-zA-Z0-9_\x7f-\xc3\xbf]*)(\\\\[a-zA-Z_\x7f-\xc3\xbf][a-zA-Z0-9_\x7f-\xc3\xbf]*" .
182+
")*'." .
183+
"\nLine: 2\n",
184+
"Element 'virtualType', attribute 'name': '' is not a valid value of the atomic type 'phpClassName'." .
185+
"\nLine: 2\n",
186+
"Element 'virtualType', attribute 'name': Warning: No precomputed value available, the value was either " .
187+
"invalid or something strange happend." .
188+
"\nLine: 2\n",
189+
],
190+
],
191+
'virtualtype with invalid_name' => [
192+
'<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
193+
<virtualType name="\\BackslashPrefix\\IsNotAllowed" type="TypeName" shared="true"/>
194+
</config>',
195+
[
196+
"Element 'virtualType', attribute 'name': [facet 'pattern'] The value '\\BackslashPrefix\\IsNotAllowed' " .
197+
"is not accepted by the pattern '" .
198+
"([a-zA-Z_\x7f-\xc3\xbf][a-zA-Z0-9_\x7f-\xc3\xbf]*)(\\\\[a-zA-Z_\x7f-\xc3\xbf][a-zA-Z0-9_\x7f-\xc3\xbf]*" .
199+
")*'." .
200+
"\nLine: 2\n",
201+
"Element 'virtualType', attribute 'name': '\\BackslashPrefix\\IsNotAllowed' " .
202+
"is not a valid value of the atomic type 'phpClassName'." .
203+
"\nLine: 2\n",
204+
"Element 'virtualType', attribute 'name': Warning: No precomputed value available, the value was either " .
205+
"invalid or something strange happend." .
206+
"\nLine: 2\n",
207+
],
208+
],
209+
'virtualtype with empty_type' => [
210+
'<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
211+
<virtualType name="Name" type="" shared="true"/>
212+
</config>',
213+
[
214+
"Element 'virtualType', attribute 'type': [facet 'pattern'] The value '' is not accepted by the pattern '" .
215+
"([a-zA-Z_\x7f-\xc3\xbf][a-zA-Z0-9_\x7f-\xc3\xbf]*)(\\\\[a-zA-Z_\x7f-\xc3\xbf][a-zA-Z0-9_\x7f-\xc3\xbf]*" .
216+
")*'." .
217+
"\nLine: 2\n",
218+
"Element 'virtualType', attribute 'type': '' is not a valid value of the atomic type 'phpClassName'." .
219+
"\nLine: 2\n",
220+
],
221+
],
222+
'virtualtype with invalid_type' => [
223+
'<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
224+
<virtualType name="777Digits\\IsNotAllowed" type="TypeName" shared="true"/>
225+
</config>',
226+
[
227+
"Element 'virtualType', attribute 'name': [facet 'pattern'] The value '777Digits\\IsNotAllowed' " .
228+
"is not accepted by the pattern '" .
229+
"([a-zA-Z_\x7f-\xc3\xbf][a-zA-Z0-9_\x7f-\xc3\xbf]*)(\\\\[a-zA-Z_\x7f-\xc3\xbf][a-zA-Z0-9_\x7f-\xc3\xbf]*" .
230+
")*'." .
231+
"\nLine: 2\n",
232+
"Element 'virtualType', attribute 'name': '777Digits\\IsNotAllowed' " .
233+
"is not a valid value of the atomic type 'phpClassName'." .
234+
"\nLine: 2\n",
235+
"Element 'virtualType', attribute 'name': Warning: No precomputed value available, the value was either " .
236+
"invalid or something strange happend." .
237+
"\nLine: 2\n",
238+
],
239+
],
175240
];

lib/internal/Magento/Framework/ObjectManager/Test/Unit/Config/_files/valid_config.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
-->
88
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
99
<preference for="Some_For_Name" type="Some_Type_Name" />
10-
<virtualType name="" type="" shared="true">
10+
<virtualType name="Php\Namespace\VirtualClassName" type="Php\Namespace\ConcreteClassName" shared="true">
1111
<arguments>
1212
<argument name="object" xsi:type="object" shared="true">Object</argument>
1313
<argument name="init_parameter" xsi:type="init_parameter">INIT_PARAMETER</argument>

lib/internal/Magento/Framework/ObjectManager/etc/config.xsd

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,20 @@
2626
</xs:complexType>
2727
</xs:redefine>
2828

29+
<xs:simpleType name="phpClassName">
30+
<xs:annotation>
31+
<xs:documentation>
32+
A string that matches a Fully Qualified Class Name from PHP, especially not starting
33+
with a backslash as this is an invalid character to start a class name with but a
34+
somewhat common mistake so this simple type can be used to validate against it
35+
already
36+
</xs:documentation>
37+
</xs:annotation>
38+
<xs:restriction base="xs:string">
39+
<xs:pattern value="([a-zA-Z_&#x7f;-&#xff;][a-zA-Z0-9_&#x7f;-&#xff;]*)(\\[a-zA-Z_&#x7f;-&#xff;][a-zA-Z0-9_&#x7f;-&#xff;]*)*"/>
40+
</xs:restriction>
41+
</xs:simpleType>
42+
2943
<xs:complexType name="init_parameter">
3044
<xs:complexContent>
3145
<xs:extension base="argumentType" />
@@ -102,8 +116,8 @@
102116
Preference help Object Manager to choose class for corresponding interface
103117
</xs:documentation>
104118
</xs:annotation>
105-
<xs:attribute name="for" type="xs:string" use="required" />
106-
<xs:attribute name="type" type="xs:string" use="required" />
119+
<xs:attribute name="for" type="phpClassName" use="required"/>
120+
<xs:attribute name="type" type="phpClassName" use="required" />
107121
</xs:complexType>
108122

109123
<xs:complexType name="typeType">
@@ -121,7 +135,7 @@
121135
</xs:element>
122136
<xs:element name="plugin" type="pluginType" minOccurs="0" maxOccurs="unbounded" />
123137
</xs:choice>
124-
<xs:attribute name="name" type="xs:string" use="required" />
138+
<xs:attribute name="name" type="phpClassName" use="required" />
125139
<xs:attribute name="shared" type="xs:boolean" use="optional" />
126140
</xs:complexType>
127141

@@ -133,14 +147,14 @@
133147
With 'virtualType' tag you can point parameters and plugins for autogenerated class
134148
</xs:documentation>
135149
</xs:annotation>
136-
<xs:attribute name="type" type="xs:string" use="optional" />
150+
<xs:attribute name="type" type="phpClassName" use="optional" />
137151
</xs:extension>
138152
</xs:complexContent>
139153
</xs:complexType>
140154

141155
<xs:complexType name="pluginType">
142156
<xs:attribute name="name" type="xs:string" use="required" />
143-
<xs:attribute name="type" type="xs:string" use="optional" />
157+
<xs:attribute name="type" type="phpClassName" use="optional" />
144158
<xs:attribute name="disabled" type="xs:boolean" use="optional" />
145159
<xs:attribute name="sortOrder" type="xs:int" use="optional" />
146160
</xs:complexType>

0 commit comments

Comments
 (0)