Your IP : 127.0.0.1
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\Framework\Code;
use Magento\Framework\Code\Generator\DefinedClasses;
use Magento\Framework\Code\Generator\EntityAbstract;
use Magento\Framework\Code\Generator\Io;
use Magento\Framework\ObjectManager\ConfigInterface;
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\Phrase;
use Magento\Framework\Filesystem\Driver\File;
use Psr\Log\LoggerInterface;
/**
* Class code generator.
*/
class Generator
{
const GENERATION_SUCCESS = 'success';
const GENERATION_ERROR = 'error';
const GENERATION_SKIP = 'skip';
/**
* @var Io
*/
protected $_ioObject;
/**
* @var array
*/
protected $_generatedEntities;
/**
* @var DefinedClasses
*/
protected $definedClasses;
/**
* @var ObjectManagerInterface
*/
protected $objectManager;
/**
* Logger instance
*
* @var LoggerInterface
*/
private $logger;
/**
* @param Generator\Io $ioObject
* @param array $generatedEntities
* @param DefinedClasses $definedClasses
* @param LoggerInterface|null $logger
*/
public function __construct(
Io $ioObject = null,
array $generatedEntities = [],
DefinedClasses $definedClasses = null,
LoggerInterface $logger = null
) {
$this->_ioObject = $ioObject ?: new Io(new File());
$this->definedClasses = $definedClasses ?: new DefinedClasses();
$this->_generatedEntities = $generatedEntities;
$this->logger = $logger;
}
/**
* Get generated entities
*
* @return array
*/
public function getGeneratedEntities()
{
return $this->_generatedEntities;
}
/**
* Set entity-to-generator map
*
* @param array $generatedEntities
* @return $this
*/
public function setGeneratedEntities($generatedEntities)
{
$this->_generatedEntities = $generatedEntities;
return $this;
}
/**
* Generate Class
*
* @param string $className
* @return string | void
* @throws \RuntimeException
* @throws \InvalidArgumentException
*/
public function generateClass($className)
{
$resultEntityType = null;
$sourceClassName = null;
foreach ($this->_generatedEntities as $entityType => $generatorClass) {
$entitySuffix = ucfirst($entityType);
// If $className string ends with $entitySuffix substring
if (strrpos($className, $entitySuffix) === strlen($className) - strlen($entitySuffix)) {
$resultEntityType = $entityType;
$sourceClassName = rtrim(
substr($className, 0, -1 * strlen($entitySuffix)),
'\\'
);
break;
}
}
if ($skipReason = $this->shouldSkipGeneration($resultEntityType, $sourceClassName, $className)) {
return $skipReason;
}
$generatorClass = $this->_generatedEntities[$resultEntityType];
/** @var EntityAbstract $generator */
$generator = $this->createGeneratorInstance($generatorClass, $sourceClassName, $className);
if ($generator !== null) {
$this->tryToLoadSourceClass($className, $generator);
if (!($file = $generator->generate())) {
/** @var $logger LoggerInterface */
$errors = $generator->getErrors();
$errors[] = 'Class ' . $className . ' generation error: The requested class did not generate properly, '
. 'because the \'generated\' directory permission is read-only. '
. 'If --- after running the \'bin/magento setup:di:compile\' CLI command when the \'generated\' '
. 'directory permission is set to write --- the requested class did not generate properly, then '
. 'you must add the generated class object to the signature of the related construct method, only.';
$message = implode(PHP_EOL, $errors);
$this->getLogger()->critical($message);
throw new \RuntimeException($message);
}
if (!$this->definedClasses->isClassLoadableFromMemory($className)) {
$this->_ioObject->includeFile($file);
}
return self::GENERATION_SUCCESS;
}
}
/**
* Retrieve logger
*
* @return LoggerInterface
*/
private function getLogger()
{
if (!$this->logger) {
$this->logger = $this->getObjectManager()->get(LoggerInterface::class);
}
return $this->logger;
}
/**
* Create entity generator
*
* @param string $generatorClass
* @param string $entityName
* @param string $className
* @return EntityAbstract
*/
protected function createGeneratorInstance($generatorClass, $entityName, $className)
{
return $this->getObjectManager()->create(
$generatorClass,
['sourceClassName' => $entityName, 'resultClassName' => $className, 'ioObject' => $this->_ioObject]
);
}
/**
* Set object manager instance.
*
* @param ObjectManagerInterface $objectManager
* @return $this
*/
public function setObjectManager(ObjectManagerInterface $objectManager)
{
$this->objectManager = $objectManager;
return $this;
}
/**
* Get object manager instance.
*
* @return ObjectManagerInterface
*/
public function getObjectManager()
{
if (!($this->objectManager instanceof ObjectManagerInterface)) {
throw new \LogicException(
"Object manager was expected to be set using setObjectManger() "
. "before getObjectManager() invocation."
);
}
return $this->objectManager;
}
/**
* Try to load/generate source class to check if it is valid or not.
*
* @param string $className
* @param EntityAbstract $generator
* @return void
* @throws \RuntimeException
*/
protected function tryToLoadSourceClass($className, $generator)
{
$sourceClassName = $generator->getSourceClassName();
if (!$this->definedClasses->isClassLoadable($sourceClassName)) {
if ($this->generateClass($sourceClassName) !== self::GENERATION_SUCCESS) {
$phrase = new Phrase(
'Source class "%1" for "%2" generation does not exist.',
[$sourceClassName, $className]
);
throw new \RuntimeException($phrase->__toString());
}
}
}
/**
* Perform validation surrounding source and result classes and entity type
*
* @param string $resultEntityType
* @param string $sourceClassName
* @param string $resultClass
* @return string|bool
*/
protected function shouldSkipGeneration($resultEntityType, $sourceClassName, $resultClass)
{
if (!$resultEntityType || !$sourceClassName) {
return self::GENERATION_ERROR;
}
/** @var ConfigInterface $omConfig */
$omConfig = $this->objectManager->get(ConfigInterface::class);
$virtualTypes = $omConfig->getVirtualTypes();
/**
* Do not try to autogenerate virtual types
* For example virtual types with names overlapping autogenerated suffixes
*/
if (isset($virtualTypes[$resultClass])) {
return self::GENERATION_SKIP;
}
if ($this->definedClasses->isClassLoadableFromDisk($resultClass)) {
$generatedFileName = $this->_ioObject->generateResultFileName($resultClass);
/**
* Must handle two edge cases: a competing process has generated the class and written it to disc already,
* or the class exists in committed code, despite matching pattern to be generated.
*/
if ($this->_ioObject->fileExists($generatedFileName)
&& !$this->definedClasses->isClassLoadableFromMemory($resultClass)
) {
$this->_ioObject->includeFile($generatedFileName);
}
return self::GENERATION_SKIP;
}
if (!isset($this->_generatedEntities[$resultEntityType])) {
throw new \InvalidArgumentException('Unknown generation entity.');
}
return false;
}
}