Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
DiCompileCommand.php
Go to the documentation of this file.
1 <?php
7 
10 use Symfony\Component\Console\Input\InputInterface;
11 use Symfony\Component\Console\Output\OutputInterface;
22 use Symfony\Component\Console\Command\Command;
23 use Symfony\Component\Console\Helper\ProgressBar;
25 
30 class DiCompileCommand extends Command
31 {
33  const NAME = 'setup:di:compile';
34 
38  private $deploymentConfig;
39 
43  private $objectManager;
44 
48  private $taskManager;
49 
53  private $directoryList;
54 
58  private $filesystem;
59 
63  private $excludedPathsList;
64 
68  private $fileDriver;
69 
73  private $componentRegistrar;
74 
86  public function __construct(
87  DeploymentConfig $deploymentConfig,
88  DirectoryList $directoryList,
89  Manager $taskManager,
90  ObjectManagerProvider $objectManagerProvider,
91  Filesystem $filesystem,
92  DriverInterface $fileDriver,
93  ComponentRegistrar $componentRegistrar
94  ) {
95  $this->deploymentConfig = $deploymentConfig;
96  $this->directoryList = $directoryList;
97  $this->objectManager = $objectManagerProvider->get();
98  $this->taskManager = $taskManager;
99  $this->filesystem = $filesystem;
100  $this->fileDriver = $fileDriver;
101  $this->componentRegistrar = $componentRegistrar;
102  parent::__construct();
103  }
104 
108  protected function configure()
109  {
110  $this->setName(self::NAME)
111  ->setDescription(
112  'Generates DI configuration and all missing classes that can be auto-generated'
113  );
114  parent::configure();
115  }
116 
122  private function checkEnvironment()
123  {
124  $messages = [];
125  $config = $this->deploymentConfig->get(ConfigOptionsListConstants::KEY_MODULES);
126  if (!$config) {
127  $messages[] = 'You cannot run this command because modules are not enabled. You can enable modules by'
128  . ' running the \'module:enable --all\' command.';
129  }
130 
131  return $messages;
132  }
133 
137  protected function execute(InputInterface $input, OutputInterface $output)
138  {
139  $errors = $this->checkEnvironment();
140  if ($errors) {
141  foreach ($errors as $line) {
142  $output->writeln($line);
143  }
144  // we must have an exit code higher than zero to indicate something was wrong
145  return Cli::RETURN_FAILURE;
146  }
147 
148  $modulePaths = $this->componentRegistrar->getPaths(ComponentRegistrar::MODULE);
149  $libraryPaths = $this->componentRegistrar->getPaths(ComponentRegistrar::LIBRARY);
150  $setupPath = $this->directoryList->getPath(DirectoryList::SETUP);
151  $generationPath = $this->directoryList->getPath(DirectoryList::GENERATED_CODE);
152 
153  $this->objectManager->get(\Magento\Framework\App\Cache::class)->clean();
154  $compiledPathsList = [
155  'application' => $modulePaths,
156  'library' => $libraryPaths,
157  'setup' => $setupPath,
158  'generated_helpers' => $generationPath
159  ];
160 
161  $this->excludedPathsList = [
162  'application' => $this->getExcludedModulePaths($modulePaths),
163  'framework' => $this->getExcludedLibraryPaths($libraryPaths),
164  'setup' => $this->getExcludedSetupPaths($setupPath),
165  ];
166  $this->configureObjectManager($output);
167 
168  $operations = $this->getOperationsConfiguration($compiledPathsList);
169 
170  try {
171  $this->cleanupFilesystem(
172  [
175  ]
176  );
177  foreach ($operations as $operationCode => $arguments) {
178  $this->taskManager->addOperation(
179  $operationCode,
180  $arguments
181  );
182  }
183 
185  $progressBar = $this->objectManager->create(
186  \Symfony\Component\Console\Helper\ProgressBar::class,
187  [
188  'output' => $output,
189  'max' => count($operations)
190  ]
191  );
192  $progressBar->setFormat(
193  '<info>%message%</info> %current%/%max% [%bar%] %percent:3s%% %elapsed% %memory:6s%'
194  );
195  $output->writeln('<info>Compilation was started.</info>');
196  $progressBar->start();
197  $progressBar->display();
198 
199  $this->taskManager->process(
200  function (OperationInterface $operation) use ($progressBar) {
201  $progressBar->setMessage($operation->getName() . '...');
202  $progressBar->display();
203  },
204  function (OperationInterface $operation) use ($progressBar) {
205  $progressBar->advance();
206  }
207  );
208 
209  $progressBar->finish();
210  $output->writeln('');
211  $output->writeln('<info>Generated code and dependency injection configuration successfully.</info>');
212  } catch (OperationException $e) {
213  $output->writeln('<error>' . $e->getMessage() . '</error>');
214  // we must have an exit code higher than zero to indicate something was wrong
215  return Cli::RETURN_FAILURE;
216  }
217  return Cli::RETURN_SUCCESS;
218  }
219 
226  private function getExcludedModulePaths(array $modulePaths)
227  {
228  $modulesByBasePath = [];
229  foreach ($modulePaths as $modulePath) {
230  $moduleDir = basename($modulePath);
231  $vendorPath = dirname($modulePath);
232  $vendorDir = basename($vendorPath);
233  $basePath = dirname($vendorPath);
234  $modulesByBasePath[$basePath][$vendorDir][] = $moduleDir;
235  }
236 
237  $basePathsRegExps = [];
238  foreach ($modulesByBasePath as $basePath => $vendorPaths) {
239  $vendorPathsRegExps = [];
240  foreach ($vendorPaths as $vendorDir => $vendorModules) {
241  $vendorPathsRegExps[] = $vendorDir
242  . '/(?:' . join('|', $vendorModules) . ')';
243  }
244  $basePathsRegExps[] = preg_quote($basePath, '#')
245  . '/(?:' . join('|', $vendorPathsRegExps) . ')';
246  }
247 
248  $excludedModulePaths = [
249  '#^(?:' . join('|', $basePathsRegExps) . ')/Test#',
250  '#^(?:' . join('|', $basePathsRegExps) . ')/tests#',
251  ];
252  return $excludedModulePaths;
253  }
254 
261  private function getExcludedLibraryPaths(array $libraryPaths)
262  {
263  $libraryPaths = array_map(function ($libraryPath) {
264  return preg_quote($libraryPath, '#');
265  }, $libraryPaths);
266 
267  $excludedLibraryPaths = [
268  '#^(?:' . join('|', $libraryPaths) . ')/([\\w]+/)?Test#',
269  '#^(?:' . join('|', $libraryPaths) . ')/([\\w]+/)?tests#',
270  ];
271  return $excludedLibraryPaths;
272  }
273 
280  private function getExcludedSetupPaths($setupPath)
281  {
282  return [
283  '#^(?:' . preg_quote($setupPath, '#') . ')(/[\\w]+)*/Test#'
284  ];
285  }
286 
293  private function cleanupFilesystem($directoryCodeList)
294  {
295  foreach ($directoryCodeList as $code) {
296  $this->filesystem->getDirectoryWrite($code)->delete();
297  }
298  }
299 
306  private function configureObjectManager(OutputInterface $output)
307  {
308  $this->objectManager->configure(
309  [
310  'preferences' => [\Magento\Setup\Module\Di\Compiler\Config\WriterInterface::class =>
311  \Magento\Setup\Module\Di\Compiler\Config\Writer\Filesystem::class,
312  ], \Magento\Setup\Module\Di\Compiler\Config\ModificationChain::class => [
313  'arguments' => [
314  'modificationsList' => [
315  'BackslashTrim' => [
316  'instance' =>
317  \Magento\Setup\Module\Di\Compiler\Config\Chain\BackslashTrim::class
318  ],
319  'PreferencesResolving' => [
320  'instance' =>
321  \Magento\Setup\Module\Di\Compiler\Config\Chain\PreferencesResolving::class
322  ],
323  'InterceptorSubstitution' => [
324  'instance' =>
325  \Magento\Setup\Module\Di\Compiler\Config\Chain\InterceptorSubstitution::class
326  ],
327  'InterceptionPreferencesResolving' => [
328  'instance' => \Magento\Setup\Module\Di\Compiler\Config\Chain\PreferencesResolving::class
329  ],
330  ]
331  ]
332  ], \Magento\Setup\Module\Di\Code\Generator\PluginList::class => [
333  'arguments' => [
334  'cache' => [
335  'instance' => \Magento\Framework\App\Interception\Cache\CompiledConfig::class
336  ]
337  ]
338  ], \Magento\Setup\Module\Di\Code\Reader\ClassesScanner::class => [
339  'arguments' => [
340  'excludePatterns' => $this->excludedPathsList
341  ]
342  ], \Magento\Setup\Module\Di\Compiler\Log\Writer\Console::class => [
343  'arguments' => [
344  'output' => $output,
345  ]
346  ],
347  ]
348  );
349  }
350 
357  private function getOperationsConfiguration(
358  array $compiledPathsList
359  ) {
360  $excludePatterns = [];
361  foreach ($this->excludedPathsList as $excludedPaths) {
362  $excludePatterns = array_merge($excludedPaths, $excludePatterns);
363  }
364 
365  $operations = [
368  'paths' => $compiledPathsList['application'],
369  ],
372  'paths' => [
373  $compiledPathsList['application'],
374  $compiledPathsList['library'],
375  $compiledPathsList['setup'],
376  $compiledPathsList['generated_helpers'],
377  ],
378  'filePatterns' => ['php' => '/\.php$/'],
379  'excludePatterns' => $excludePatterns,
380  ],
382  'intercepted_paths' => [
383  $compiledPathsList['application'],
384  $compiledPathsList['library'],
385  $compiledPathsList['generated_helpers'],
386  ],
387  'path_to_store' => $compiledPathsList['generated_helpers'],
388  ],
390  $compiledPathsList['application'],
391  $compiledPathsList['library'],
392  $compiledPathsList['generated_helpers'],
393  ],
395  $compiledPathsList['application'],
396  $compiledPathsList['library'],
397  $compiledPathsList['generated_helpers'],
398  ]
399  ];
400 
401  return $operations;
402  }
403 }
$componentRegistrar
Definition: bootstrap.php:23
$operations
Definition: bulk.php:55
$config
Definition: fraud_order.php:17
if(!file_exists(VENDOR_PATH)) $vendorDir
Definition: autoload.php:25
$deploymentConfig
__construct(DeploymentConfig $deploymentConfig, DirectoryList $directoryList, Manager $taskManager, ObjectManagerProvider $objectManagerProvider, Filesystem $filesystem, DriverInterface $fileDriver, ComponentRegistrar $componentRegistrar)
$arguments
if(isset($opts->l)) $libraryPath
$filesystem
$errors
Definition: overview.phtml:9
$code
Definition: info.phtml:12