Skip to content

Instantly share code, notes, and snippets.

@grachevko
Last active May 4, 2020 18:06
Show Gist options
  • Save grachevko/3a3be4437c35da1e88684b8b85aaa0da to your computer and use it in GitHub Desktop.
Save grachevko/3a3be4437c35da1e88684b8b85aaa0da to your computer and use it in GitHub Desktop.
<?php
require_once __DIR__.'/vendor/autoload.php';
Symfony\Component\ErrorHandler\Debug::enable();
$arr = Symfony\Component\Yaml\Yaml::parse(\file_get_contents(__DIR__.'/config/packages/rpc_server.yaml'));
$dirs = \array_merge(...\array_values($arr['rpc_server']['mapping']));
$dirs = \array_map(fn (string $path) => \str_replace('%kernel.project_dir%', __DIR__, $path), $dirs);
$dirs = \array_filter($dirs, fn (string $path) => \file_exists($path));
\asort($dirs);
$dirs = \array_unique($dirs);
$dirs = \array_values($dirs);
$reader = new Doctrine\Common\Annotations\AnnotationReader();
$filesystem = new Symfony\Component\Filesystem\Filesystem();
$namespaceClosure = static function (string $fqcn) {
$fqcn = \str_replace([
'\\Presentation',
'\\V1',
'\\V2',
'\\Rpc',
'\\Method',
'\\Api',
], '', $fqcn);
$array = \explode('\\', $fqcn);
\array_pop($array);
return \implode('\\', [
\array_shift($array),
\array_shift($array),
'Ports',
'Rpc',
...$array,
]);
};
$addUse = static function (Laminas\Code\Generator\TraitUsageInterface $generator, string $use): void {
if (0 === \strpos($use, 'Timiki')) {
return;
}
[$class, $alias] = false === \strpos(' ', $use)
? [$use, null]
: \explode(' ', \str_replace(' as ', ' ', $use));
if (!\class_exists($class) && !\interface_exists($class)) {
return;
}
$generator->addUse($class, $alias);
};
$handled = [];
foreach ($dirs as $dir) {
foreach (Symfony\Component\Finder\Finder::create()->in($dir)->files() as $item) {
$filePath = $item->getPathname();
if (\array_key_exists($filePath, $handled)) {
continue;
}
$handled[$filePath] = true;
$fileGenerator = Laminas\Code\Generator\FileGenerator::fromReflectedFileName($filePath);
$classGenerator = $fileGenerator->getClass();
$classFQCN = $classGenerator->getNamespaceName().'\\'.$classGenerator->getName();
$reflectionClass = new ReflectionClass($classFQCN);
$rpcMethod = null;
$rpcRoles = [];
foreach ($reader->getClassAnnotations($reflectionClass) as $classAnnotation) {
if ($classAnnotation instanceof Timiki\Bundle\RpcServerBundle\Mapping\Method) {
$rpcMethod = $classAnnotation->value;
}
if ($classAnnotation instanceof Timiki\Bundle\RpcServerBundle\Mapping\Roles) {
$rpcRoles = $classAnnotation->value;
}
}
if (null === $rpcMethod) {
continue;
}
$executeMethodGenerator = null;
foreach ($reflectionClass->getMethods() as $reflectionMethod) {
foreach ($reader->getMethodAnnotations($reflectionMethod) as $methodAnnotation) {
if ($methodAnnotation instanceof Timiki\Bundle\RpcServerBundle\Mapping\Execute) {
$executeMethodGenerator = $classGenerator->getMethod($reflectionMethod->getName());
break;
}
}
}
if (null === $executeMethodGenerator) {
dump('Execute method not found for '.$classFQCN);
continue;
}
$isCommand = null === $executeMethodGenerator->getReturnType();
$handlerNamespace = $namespaceClosure($classFQCN);
$handlerGenerator = (new Laminas\Code\Generator\ClassGenerator())
->setNamespaceName($handlerNamespace)
->setName($classGenerator->getName())
->setFinal(true)
// ->setDocBlock(
// (new Laminas\Code\Generator\DocBlockGenerator())
// ->setTag(['name' => 'see \\'.$classFQCN])
// )
->addUse(App\Core\JSONRPC\RPC::class);
foreach ($classGenerator->getUses() as $use) {
$addUse($handlerGenerator, $use);
}
$handlerInputConstructorGenerator = (new \Laminas\Code\Generator\MethodGenerator('__construct'))
->setVisibility('private');
$handlerInputGenerator = (new Laminas\Code\Generator\ClassGenerator())
->setNamespaceName($handlerNamespace)
->setName($handlerGenerator->getName().($isCommand ? 'Command' : 'Query'))
->addUse('Symfony\Component\Validator\Constraints', 'Assert')
->addMethodFromGenerator($handlerInputConstructorGenerator);
$handlerInvokeParameterName = $isCommand ? 'command' : 'query';
$handlerInvokeGenerator = new Laminas\Code\Generator\MethodGenerator('__invoke');
if (null !== $executeMethodGenerator->getReturnType()) {
$handlerInvokeGenerator->setReturnType($executeMethodGenerator->getReturnType()->generate());
}
$endpoint = null;
if (0 === \strpos($classFQCN, 'App\\CRM')) {
$endpoint = 'crm';
} elseif (0 === \strpos($classFQCN, 'App\\PersonalCabinet')) {
$endpoint = 'pc';
} elseif (0 === \strpos($classFQCN, 'App\\Security')) {
$endpoint = 'security';
}
if (null === $endpoint) {
dd('Not found Endpoint for '.$classFQCN);
}
$version = 1;
if (false !== \strpos($classFQCN, '\\V2\\')) {
$version = 2;
}
$invokeDocBlocks = [];
$invokeDocBlocks[] = \sprintf(
'@RPC(endpoint="%s", method="%s", version="%s")',
$endpoint,
$rpcMethod,
$version
);
if ([] !== $rpcRoles) {
$handlerGenerator->addUse(Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted::class);
$invokeDocBlocks[] = '';
}
foreach ($rpcRoles as $rpcRole) {
$invokeDocBlocks[] = \sprintf('@IsGranted("%s")', $rpcRole);
}
$handlerInvokeGenerator->setDocBlock(\implode(PHP_EOL, $invokeDocBlocks));
$handlerInvokeGenerator->setBody($executeMethodGenerator->getBody());
$handlerConstructorGenerator = new Laminas\Code\Generator\MethodGenerator('__construct');
$handlerGenerator->addMethodFromGenerator($handlerInvokeGenerator);
foreach ($reflectionClass->getMethods() as $reflectionMethod) {
if ($executeMethodGenerator->getName() === $reflectionMethod->getName()) {
continue;
}
if (\in_array($reflectionMethod->name, ['getContainer', 'setContainer'], true)) {
continue;
}
$methodGenerator = Laminas\Code\Generator\MethodGenerator::fromReflection(
new Laminas\Code\Reflection\MethodReflection($reflectionMethod->class, $reflectionMethod->name)
);
$returnTypeGenerator = $methodGenerator->getReturnType();
if (null !== $returnTypeGenerator) {
$dependencyClass = $returnTypeGenerator->type;
$addUse($handlerGenerator, $dependencyClass);
$addUse($handlerInputGenerator, $dependencyClass);
}
$normalizedMethodBody = \trim(\preg_replace('/\n\s+/', '', $methodGenerator->getBody()));
if ('getTokenStorage' === $methodGenerator->getName()) {
continue;
}
if ('getUser' === $methodGenerator->getName()) {
if (false === \strpos($handlerInvokeGenerator->getBody(), 'this->getUser')) {
continue;
}
$handlerInvokeGenerator->setParameter(
(new \Laminas\Code\Generator\ParameterGenerator())
->setName('user')
->setType(App\Security\Application\UserData::class)
)
->setBody(
\str_replace(
'this->getUser()',
'user',
$handlerInvokeGenerator->getBody()
)
);
continue;
}
if (false !== \strpos($normalizedMethodBody, 'getContainer()->get')) {
if ([] !== $reflectionMethod->getParameters()) {
dd('Looks like method require parameters.');
}
$dependencyName = Symfony\Component\String\u((new ReflectionClass($dependencyClass))->getShortName())->camel()->toString();
$dependencyName = \str_replace('Interface', '', $dependencyName);
$handlerInvokeGenerator->setBody(
\str_replace(
\sprintf('$this->%s()', $methodGenerator->getName()),
\sprintf('$this->%s', $dependencyName),
$handlerInvokeGenerator->getBody(),
)
);
$handlerGenerator->addPropertyFromGenerator(
(new Laminas\Code\Generator\PropertyGenerator())
->setName($dependencyName)
->setType($dependencyClass)
->setVisibility('private')
);
$handlerConstructorGenerator->setParameter(
(new Laminas\Code\Generator\ParameterGenerator())
->setName($dependencyName)
->setType($dependencyClass)
);
$handlerConstructorGenerator->setBody(
$handlerConstructorGenerator->getBody()
.PHP_EOL
.\sprintf('$this->%s = $%s;', $dependencyName, $dependencyName)
);
continue;
}
$methodGenerator->setVisibility('public');
$methodGenerator->setFinal(false);
foreach ((\Laminas\Code\Generator\FileGenerator::fromReflectedFileName(
$reflectionMethod->getFileName()
))->getUses() as [$class, $alias]) {
$addUse($handlerGenerator, $class);
$addUse($handlerInputGenerator, $class);
if (null === $alias) {
continue;
}
$arr = \explode('\\', $class);
$name = \array_pop($arr);
$methodGenerator->setBody(\str_replace(
[\sprintf(' %s(', $alias), \sprintf(' %s::', $alias)],
[\sprintf(' %s(', $name), \sprintf(' %s::', $name)],
$methodGenerator->getBody())
);
}
$handlerInputGenerator->addMethodFromGenerator($methodGenerator);
$handlerInvokeGenerator->setBody(
\str_replace(
\sprintf('$this->%s(', $methodGenerator->getName()),
\sprintf('$%s->%s(', $handlerInvokeParameterName, $methodGenerator->getName()),
$handlerInvokeGenerator->getBody(),
)
);
}
foreach ($reflectionClass->getProperties() as $reflectionProperty) {
$handlerInvokeGenerator->setBody(
\str_replace(
\sprintf('$this->%s', $reflectionProperty->name),
\sprintf('$%s->%s', $handlerInvokeParameterName, $reflectionProperty->name),
$handlerInvokeGenerator->getBody(),
)
);
$type = (static function (string $doc) {
$varLine = null;
foreach (\explode(PHP_EOL, $doc) as $row) {
if (false !== \strpos($row, '@var')) {
$varLine = $row;
break;
}
}
$varLine = \str_replace(['@var', '*', 'null'], '', $varLine);
$varLine = \trim($varLine);
$varLine = \trim($varLine, '|[]');
return \trim($varLine);
})($reflectionProperty->getDocComment());
$parameterGenerator = (new \Laminas\Code\Generator\ParameterGenerator($reflectionProperty->name));
if (null !== $type) {
$parameterGenerator->setType($type);
}
$handlerInputConstructorGenerator
->setParameter($parameterGenerator)
->setBody(
$handlerInputConstructorGenerator->getBody()
.PHP_EOL.
\sprintf('$this->%s = $%s;', $reflectionProperty->name, $reflectionProperty->name)
);
$handlerInputGenerator
->addPropertyFromGenerator(
(new Laminas\Code\Generator\PropertyGenerator())
->setName($reflectionProperty->name)
->setVisibility('public')
->setDocBlock((static function (string $docBlock): string {
$docBlock = \preg_replace('/.+@Rpc.+\n/', '', $docBlock);
$rows = [];
foreach (\explode(PHP_EOL, $docBlock) as $row) {
$row = \trim($row);
if ('/**' === $row || '*/' === $row) {
continue;
}
$row = \trim(\ltrim($row, '*'));
if ('' === $row) {
continue;
}
$rows[] = $row;
if (0 === \strpos($row, '@var')) {
$rows[] = '';
}
}
return \implode(PHP_EOL, $rows);
})($reflectionProperty->getDocComment()))
);
}
$folder = __DIR__.'/src/'.\str_replace(['App\\', '\\'], ['', '/'], $handlerGenerator->getNamespaceName()).'/';
$filesystem->mkdir($folder);
if ([] !== $handlerInputGenerator->getProperties()) {
$handlerInvokeGenerator->setParameter(
(new Laminas\Code\Generator\ParameterGenerator($handlerInvokeParameterName))
->setType($handlerNamespace.'\\'.$handlerInputGenerator->getName())
);
\file_put_contents($folder.$handlerInputGenerator->getName().'.php', '<?php'.PHP_EOL.$handlerInputGenerator->generate());
// @unlink($folder.$handlerInputGenerator->getName().'.php');
}
if ('' !== $handlerConstructorGenerator->getBody()) {
$handlerGenerator->addMethodFromGenerator($handlerConstructorGenerator);
}
// @unlink($folder.$handlerGenerator->getName().'.php');
\file_put_contents($folder.$handlerGenerator->getName().'.php', '<?php'.PHP_EOL.$handlerGenerator->generate());
}
$filesystem->remove($dir);
break;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment