Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
ExtensibleInterfacesTest.php
Go to the documentation of this file.
1 <?php
7 
9 
16 class ExtensibleInterfacesTest extends \PHPUnit\Framework\TestCase
17 {
18  const EXTENSIBLE_DATA_INTERFACE = \Magento\Framework\Api\ExtensibleDataInterface::class;
19 
24  {
25  $invoker = new \Magento\Framework\App\Utility\AggregateInvoker($this);
26  $invoker(
30  function ($filename) {
31  $errors = [];
32  $fileContent = file_get_contents($filename);
33  $pattern = '/'
34  . str_replace('\\', '\\\\', self::EXTENSIBLE_DATA_INTERFACE)
35  . '/';
36  $extendsFromExtensibleDataInterface = preg_match($pattern, $fileContent);
37  $namespacePattern = '/namespace ([\w\\\\]+).*interface ([\w\\\\]+)/s';
38  if ($extendsFromExtensibleDataInterface
39  && preg_match($namespacePattern, $fileContent, $matches)
40  ) {
41  $namespace = $matches[1];
42  $interfaceName = $matches[2];
43  $fullInterfaceName = '\\' . $namespace . '\\' . $interfaceName;
44  $interfaceReflection = new \ReflectionClass($fullInterfaceName);
45  if ($interfaceReflection->isSubclassOf(self::EXTENSIBLE_DATA_INTERFACE)) {
46  $interfaceName = '\\' . $interfaceReflection->getName();
47  $extensionClassName = substr($interfaceName, 0, -strlen('Interface')) . 'Extension';
48  $extensionInterfaceName = $extensionClassName . 'Interface';
49 
51  $errors = $this->checkGetExtensionAttributes(
52  $interfaceReflection,
53  $extensionInterfaceName,
54  $fullInterfaceName
55  );
56 
58  $errors = array_merge(
59  $errors,
60  $this->checkSetExtensionAttributes(
61  $interfaceReflection,
62  $extensionInterfaceName,
63  $fullInterfaceName
64  )
65  );
66  }
67  }
68 
69  $this->assertEmpty(
70  $errors,
71  "Error validating $filename\n" . print_r($errors, true)
72  );
73  },
74  $this->getInterfacesFiles()
75  );
76  }
77 
86  private function checkGetExtensionAttributes(
87  \ReflectionClass $interfaceReflection,
88  $extensionInterfaceName,
89  $fullInterfaceName
90  ) {
91  $errors = [];
92  try {
93  $methodReflection = $interfaceReflection->getMethod('getExtensionAttributes');
95  $methodDocBlock = $methodReflection->getDocComment();
96  $pattern = "/@return\s+" . str_replace('\\', '\\\\', $extensionInterfaceName) . "/";
97  if (!preg_match($pattern, $methodDocBlock)) {
98  $errors[] =
99  "'{$fullInterfaceName}::getExtensionAttributes()' must be declared "
100  . "with a return type of '{$extensionInterfaceName}'.";
101  }
102  } catch (\ReflectionException $e) {
103  $errors[] = "The following method should be declared in "
104  . "'{$extensionInterfaceName}'. '{$extensionInterfaceName}' must be specified as"
105  . " a return type for '{$fullInterfaceName}::getExtensionAttributes()'";
106  }
107 
108  return $errors;
109  }
110 
119  private function checkSetExtensionAttributes(
120  \ReflectionClass $interfaceReflection,
121  $extensionInterfaceName,
122  $fullInterfaceName
123  ) {
124  $errors = [];
125  try {
126  $methodReflection = $interfaceReflection->getMethod('setExtensionAttributes');
128  $methodParameters = $methodReflection->getParameters();
129 
130  if (empty($methodParameters)) {
131  $errors[] = "'{$extensionInterfaceName}' must be specified as the parameter type "
132  . "in '{$fullInterfaceName}::setExtensionAttributes()'.";
133  } else {
134  // Get the parameter name via a regular expression capture because the class may
135  // not exist which causes a fatal error
136  preg_match('/\[\s<\w+?>\s([\w]+)/s', $methodParameters[0]->__toString(), $matches);
137  $isCorrectParameter = false;
138  if (isset($matches[1]) && '\\' . $matches[1] != $extensionInterfaceName) {
139  $isCorrectParameter = true;
140  }
141 
142  if (!$isCorrectParameter) {
143  $errors[] = "'{$extensionInterfaceName}' must be specified as the parameter type "
144  . "in '{$fullInterfaceName}::setExtensionAttributes()'.";
145  }
146  }
147  } catch (\ReflectionException $e) {
148  $errors[] = "'{$fullInterfaceName}::setExtensionAttributes()' must be declared "
149  . "with a '{$extensionInterfaceName}' parameter type.";
150  }
151 
152  return $errors;
153  }
154 
159  {
160  $invoker = new \Magento\Framework\App\Utility\AggregateInvoker($this);
161  $invoker(
165  function ($filename) {
166  $errors = [];
167  $fileContent = file_get_contents($filename);
168  $extensibleClassPattern = 'class [^\{]+extends[^\{]+AbstractExtensible';
169  $abstractExtensibleClassPattern = 'abstract ' . $extensibleClassPattern;
170  if (preg_match('/' . $extensibleClassPattern . '/', $fileContent) &&
171  !preg_match('/' . $abstractExtensibleClassPattern . '/', $fileContent)
172  ) {
173  $fileReflection = new \Zend\Code\Reflection\FileReflection($filename, true);
174  foreach ($fileReflection->getClasses() as $classReflection) {
175  if ($classReflection->isSubclassOf(self::EXTENSIBLE_DATA_INTERFACE)) {
176  $methodsToCheck = ['setExtensionAttributes', 'getExtensionAttributes'];
177  foreach ($methodsToCheck as $methodName) {
178  try {
179  $classReflection->getMethod($methodName);
180  } catch (\ReflectionException $e) {
181  $className = $classReflection->getName();
182  $errors[] = "'{$className}::{$methodName}()' must be declared or "
183  . "'{$className}' should not be inherited from extensible class.";
184  }
185  }
186  }
187  }
188  }
189 
190  $this->assertEmpty(
191  $errors,
192  "Error validating $filename\n" . print_r($errors, true)
193  );
194  },
195  $this->getPhpFiles()
196  );
197  }
198 
204  public function getInterfacesFiles()
205  {
206  $codeInterfaceFiles = $this->getFiles(BP . '/app', '*Interface.php');
207  $libInterfaceFiles = $this->getFiles(BP . '/lib/Magento', '*Interface.php');
208  $interfaces = [];
209  $filesToCheck = $this->blacklistFilter(array_merge($codeInterfaceFiles, $libInterfaceFiles));
210  foreach ($filesToCheck as $file) {
211  $interfaces[substr($file, strlen(BP))] = [$file];
212  }
213  return $interfaces;
214  }
215 
221  public function getPhpFiles()
222  {
223  $codeFiles = $this->getFiles(BP . '/app', '*.php');
224  $libFiles = $this->getFiles(BP . '/lib/Magento', '*.php');
225  $phpFiles = [];
226  $filesToCheck = $this->blacklistFilter(array_merge($codeFiles, $libFiles));
227  foreach ($filesToCheck as $file) {
228  $phpFiles[substr($file, strlen(BP))] = [$file];
229  }
230  return $phpFiles;
231  }
232 
240  protected function getFiles($dir, $pattern)
241  {
242  $files = glob($dir . '/' . $pattern, GLOB_NOSORT);
243  foreach (glob($dir . '/*', GLOB_ONLYDIR | GLOB_NOSORT) as $newDir) {
244  $files = array_merge($files, $this->getFiles($newDir, $pattern));
245  }
246  return $files;
247  }
248 
255  protected function blacklistFilter($preFilter)
256  {
257  $postFilter = [];
258  $blacklist = Files::init()->readLists(__DIR__ . '/_files/ExtensibleInterfacesTest/blacklist*');
259  foreach ($preFilter as $file) {
260  if (!in_array($file, $blacklist)) {
261  $postFilter[] = $file;
262  }
263  }
264  return $postFilter;
265  }
266 }
$pattern
Definition: website.php:22
defined('TESTS_BP')||define('TESTS_BP' __DIR__
Definition: _bootstrap.php:60
const BP
Definition: autoload.php:14
foreach($appDirs as $dir) $files
$errors
Definition: overview.phtml:9
if($currentSelectedMethod==$_code) $className
Definition: form.phtml:31