Last active
November 4, 2020 06:57
-
-
Save niisan-tokyo/7ca552da7ea3d73bd553513b048807e1 to your computer and use it in GitHub Desktop.
interfaceから実装を作るジェネレータ
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Niisan\App\Generator; | |
use Reflection; | |
use ReflectionClass; | |
use ReflectionMethod; | |
use RuntimeException; | |
class ImplementGenerator | |
{ | |
const INDENT = 4; | |
private $defaultNameSpace = 'Niisan\\App'; | |
private ReflectionClass $interface; | |
private $uses; | |
public function from(string $class_name): self | |
{ | |
$reflection = new ReflectionClass($class_name); | |
if ($reflection->isInterface() === false) { | |
throw new RuntimeException('interfaceではない'); | |
} | |
$this->interface = $reflection; | |
return $this; | |
} | |
public function output(string $filename) | |
{ | |
$interface_name = $this->interface->getName(); | |
$this->uses[] = $interface_name; | |
$methods = $this->analyzeMethods(); | |
$text = $this->buildText($filename, $methods); | |
file_put_contents('src/' . $filename . '.php', $text); | |
} | |
private function analyzeMethods(): array | |
{ | |
$methods = []; | |
foreach ($this->interface->getMethods() as $method) { | |
$methods[] = $this->analyzeMethod($method); | |
} | |
return $methods; | |
} | |
private function analyzeMethod(ReflectionMethod $method) | |
{ | |
$method_name = $method->getName(); | |
$modifier = Reflection::getModifierNames($method->getModifiers()); | |
$modifier = array_filter($modifier, fn($name) => $name !== 'abstract'); | |
$return_type = ($method->hasReturnType()) ? $this->removeNamespace($method->getReturnType()->__toString()) : null; | |
$args = []; | |
foreach ($method->getParameters() as $param) { | |
$arg = [ | |
'type' => null, | |
'optional' => $param->isOptional(), | |
'name' => $param->getName(), | |
]; | |
if ($param->hasType()) { | |
$type = $param->getType(); | |
$optional = (strpos($type->__toString(), '?') === 0 and $type->isBuiltin() === false) ? '?': ''; | |
$arg['type'] = $optional . $this->removeNamespace($type->__toString()); | |
if ($type->isBuiltin() === false) { | |
$this->uses[] = ltrim($type->__toString(), '?'); | |
} | |
} | |
if ($arg['optional']) { | |
$default = $param->getDefaultValue(); | |
$arg['default'] = is_string($default)? "'$default'": $default; | |
} | |
$args[] = $arg; | |
} | |
return [ | |
'name' => $method_name, | |
'modifier' => implode(' ', $modifier), | |
'return_type' => $return_type, | |
'args' => $args | |
]; | |
} | |
private function buildText(string $filename, array $methods) | |
{ | |
$text = "<?php\n"; | |
$text .= 'namespace ' . $this->getNameSpace($filename) . ";\n\n"; | |
$this->uses = array_unique($this->uses); | |
foreach ($this->uses as $use) { | |
$text .= 'use ' . $use . ";\n"; | |
} | |
$text .= "\n" . 'class ' . $this->getClassName($filename) . ' implements ' . $this->removeNamespace($this->interface->getName()); | |
$text .= "\n{"; | |
$indent = str_repeat(' ', self::INDENT); | |
foreach ($methods as $method) { | |
$text .= "\n" . $indent . $method['modifier'] . ' function ' . $method['name'] . '('; | |
$args = []; | |
foreach ($method['args'] as $arg) { | |
$args[] .= (($arg['type']) ? $arg['type'] . ' ' : '') | |
. ('$' . $arg['name']) | |
. (($arg['optional']) ? ' = ' . $arg['default'] : ''); | |
} | |
$text .= implode(', ', $args) . ')'; | |
$text .= ($method['return_type']) ? ': ' . $method['return_type'] : ''; | |
$text .= "\n" . $indent . "{\n\n" . $indent . "}\n"; | |
} | |
$text .= '}'; | |
return $text; | |
} | |
private function getNameSpace(string $filename) | |
{ | |
$segment = explode('/', $filename); | |
if (count($segment) === 1) { | |
return $this->defaultNameSpace; | |
} | |
$name = $this->defaultNameSpace; | |
foreach ($segment as $key => $val) { | |
if ($key === count($segment) - 1) { | |
break; | |
} | |
$name .= '\\' . $val; | |
} | |
return $name; | |
} | |
private function getClassName(string $filename) | |
{ | |
$segment = explode('/', $filename); | |
return $segment[count($segment) - 1]; | |
} | |
private function removeNamespace(string $class): string | |
{ | |
$segment = explode('\\', $class); | |
return $segment[count($segment) - 1]; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment