vendor/symfony/maker-bundle/src/Maker/MakeUser.php line 234
<?php/** This file is part of the Symfony MakerBundle package.** (c) Fabien Potencier <fabien@symfony.com>** For the full copyright and license information, please view the LICENSE* file that was distributed with this source code.*/namespace Symfony\Bundle\MakerBundle\Maker;use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;use Symfony\Bundle\MakerBundle\ConsoleStyle;use Symfony\Bundle\MakerBundle\DependencyBuilder;use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper;use Symfony\Bundle\MakerBundle\Doctrine\EntityClassGenerator;use Symfony\Bundle\MakerBundle\Doctrine\ORMDependencyBuilder;use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;use Symfony\Bundle\MakerBundle\FileManager;use Symfony\Bundle\MakerBundle\Generator;use Symfony\Bundle\MakerBundle\InputConfiguration;use Symfony\Bundle\MakerBundle\Security\SecurityConfigUpdater;use Symfony\Bundle\MakerBundle\Security\UserClassBuilder;use Symfony\Bundle\MakerBundle\Security\UserClassConfiguration;use Symfony\Bundle\MakerBundle\Util\ClassSourceManipulator;use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator;use Symfony\Bundle\MakerBundle\Util\YamlManipulationFailedException;use Symfony\Bundle\MakerBundle\Validator;use Symfony\Bundle\SecurityBundle\SecurityBundle;use Symfony\Component\Console\Command\Command;use Symfony\Component\Console\Input\InputArgument;use Symfony\Component\Console\Input\InputInterface;use Symfony\Component\Console\Input\InputOption;use Symfony\Component\Security\Core\Exception\UnsupportedUserException;use Symfony\Component\Security\Core\Exception\UserNotFoundException;use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;use Symfony\Component\Security\Core\User\UserInterface;use Symfony\Component\Security\Core\User\UserProviderInterface;use Symfony\Component\Yaml\Yaml;/*** @author Ryan Weaver <weaverryan@gmail.com>** @internal*/final class MakeUser extends AbstractMaker{public function __construct(private FileManager $fileManager,private UserClassBuilder $userClassBuilder,private SecurityConfigUpdater $configUpdater,private EntityClassGenerator $entityClassGenerator,private DoctrineHelper $doctrineHelper,) {}public static function getCommandName(): string{return 'make:user';}public static function getCommandDescription(): string{return 'Creates a new security user class';}public function configureCommand(Command $command, InputConfiguration $inputConfig): void{$command->addArgument('name', InputArgument::OPTIONAL, 'The name of the security user class (e.g. <fg=yellow>User</>)')->addOption('is-entity', null, InputOption::VALUE_NONE, 'Do you want to store user data in the database (via Doctrine)?')->addOption('identity-property-name', null, InputOption::VALUE_REQUIRED, 'Enter a property name that will be the unique "display" name for the user (e.g. <comment>email, username, uuid</comment>)')->addOption('with-password', null, InputOption::VALUE_NONE, 'Will this app be responsible for checking the password? Choose <comment>No</comment> if the password is actually checked by some other system (e.g. a single sign-on server)')->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeUser.txt'));$inputConfig->setArgumentAsNonInteractive('name');}public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void{if (null === $input->getArgument('name')) {$name = $io->ask($command->getDefinition()->getArgument('name')->getDescription(),'User');$input->setArgument('name', $name);}$userIsEntity = $io->confirm('Do you want to store user data in the database (via Doctrine)?',class_exists(DoctrineBundle::class));if ($userIsEntity) {$dependencies = new DependencyBuilder();ORMDependencyBuilder::buildDependencies($dependencies);$missingPackagesMessage = $dependencies->getMissingPackagesMessage(self::getCommandName(), 'Doctrine must be installed to store user data in the database');if ($missingPackagesMessage) {throw new RuntimeCommandException($missingPackagesMessage);}}$input->setOption('is-entity', $userIsEntity);$identityFieldName = $io->ask('Enter a property name that will be the unique "display" name for the user (e.g. <comment>email, username, uuid</comment>)', 'email', [Validator::class, 'validatePropertyName']);$input->setOption('identity-property-name', $identityFieldName);$io->text('Will this app need to hash/check user passwords? Choose <comment>No</comment> if passwords are not needed or will be checked/hashed by some other system (e.g. a single sign-on server).');$userWillHavePassword = $io->confirm('Does this app need to hash/check user passwords?');$input->setOption('with-password', $userWillHavePassword);}public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void{$userClassConfiguration = new UserClassConfiguration($input->getOption('is-entity'),$input->getOption('identity-property-name'),$input->getOption('with-password'));$userClassNameDetails = $generator->createClassNameDetails($input->getArgument('name'),$userClassConfiguration->isEntity() ? 'Entity\\' : 'Security\\');// A) Generate the User classif ($userClassConfiguration->isEntity()) {$classPath = $this->entityClassGenerator->generateEntityClass($userClassNameDetails,false, // api resource$userClassConfiguration->hasPassword() // security user);} else {$classPath = $generator->generateClass($userClassNameDetails->getFullName(), 'Class.tpl.php');}// need to write changes early so we can modify the contents below$generator->writeChanges();$entityUsesAttributes = ($isEntity = $userClassConfiguration->isEntity()) && $this->doctrineHelper->doesClassUsesAttributes($userClassNameDetails->getFullName());if ($isEntity && !$entityUsesAttributes) {throw new \RuntimeException('MakeUser only supports attribute mapping with doctrine entities.');}// B) Implement UserInterface$manipulator = new ClassSourceManipulator(sourceCode: $this->fileManager->getFileContents($classPath),overwrite: true,useAttributesForDoctrineMapping: $entityUsesAttributes);$manipulator->setIo($io);$this->userClassBuilder->addUserInterfaceImplementation($manipulator, $userClassConfiguration);$generator->dumpFile($classPath, $manipulator->getSourceCode());// C) Generate a custom user provider, if necessaryif (!$userClassConfiguration->isEntity()) {$userClassConfiguration->setUserProviderClass($generator->getRootNamespace().'\\Security\\UserProvider');$useStatements = new UseStatementGenerator([UnsupportedUserException::class,UserNotFoundException::class,PasswordAuthenticatedUserInterface::class,PasswordUpgraderInterface::class,UserInterface::class,UserProviderInterface::class,]);$customProviderPath = $generator->generateClass($userClassConfiguration->getUserProviderClass(),'security/UserProvider.tpl.php',['use_statements' => $useStatements,'user_short_name' => $userClassNameDetails->getShortName(),]);}// D) Update security.yaml$securityYamlUpdated = false;$path = 'config/packages/security.yaml';if ($this->fileManager->fileExists($path)) {try {$newYaml = $this->configUpdater->updateForUserClass($this->fileManager->getFileContents($path),$userClassConfiguration,$userClassNameDetails->getFullName());$generator->dumpFile($path, $newYaml);$securityYamlUpdated = true;} catch (YamlManipulationFailedException) {}}$generator->writeChanges();$this->writeSuccessMessage($io);$io->text('Next Steps:');$nextSteps = [sprintf('Review your new <info>%s</info> class.', $userClassNameDetails->getFullName()),];if ($userClassConfiguration->isEntity()) {$nextSteps[] = sprintf('Use <comment>make:entity</comment> to add more fields to your <info>%s</info> entity and then run <comment>make:migration</comment>.',$userClassNameDetails->getShortName());} else {$nextSteps[] = sprintf('Open <info>%s</info> to finish implementing your user provider.',$this->fileManager->relativizePath($customProviderPath));}if (!$securityYamlUpdated) {$yamlExample = $this->configUpdater->updateForUserClass('security: {}',$userClassConfiguration,$userClassNameDetails->getFullName());$nextSteps[] = "Your <info>security.yaml</info> could not be updated automatically. You'll need to add the following config manually:\n\n".$yamlExample;}$nextSteps[] = 'Create a way to authenticate! See https://symfony.com/doc/current/security.html';$nextSteps = array_map(static fn ($step) => sprintf(' - %s', $step), $nextSteps);$io->text($nextSteps);}public function configureDependencies(DependencyBuilder $dependencies, InputInterface $input = null): void{// checking for SecurityBundle guarantees security.yaml is present$dependencies->addClassDependency(SecurityBundle::class,'security');// needed to update the YAML files$dependencies->addClassDependency(Yaml::class,'yaml');if (null !== $input && $input->getOption('is-entity')) {ORMDependencyBuilder::buildDependencies($dependencies);}}}