1<?php 2 3/* 4 * This file is part of the Prophecy. 5 * (c) Konstantin Kudryashov <ever.zet@gmail.com> 6 * Marcello Duarte <marcello.duarte@gmail.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Prophecy\Doubler\Generator; 13 14/** 15 * Class code creator. 16 * Generates PHP code for specific class node tree. 17 * 18 * @author Konstantin Kudryashov <ever.zet@gmail.com> 19 */ 20class ClassCodeGenerator 21{ 22 /** 23 * @var TypeHintReference 24 */ 25 private $typeHintReference; 26 27 public function __construct(TypeHintReference $typeHintReference = null) 28 { 29 $this->typeHintReference = $typeHintReference ?: new TypeHintReference(); 30 } 31 32 /** 33 * Generates PHP code for class node. 34 * 35 * @param string $classname 36 * @param Node\ClassNode $class 37 * 38 * @return string 39 */ 40 public function generate($classname, Node\ClassNode $class) 41 { 42 $parts = explode('\\', $classname); 43 $classname = array_pop($parts); 44 $namespace = implode('\\', $parts); 45 46 $code = sprintf("class %s extends \%s implements %s {\n", 47 $classname, $class->getParentClass(), implode(', ', 48 array_map(function ($interface) {return '\\'.$interface;}, $class->getInterfaces()) 49 ) 50 ); 51 52 foreach ($class->getProperties() as $name => $visibility) { 53 $code .= sprintf("%s \$%s;\n", $visibility, $name); 54 } 55 $code .= "\n"; 56 57 foreach ($class->getMethods() as $method) { 58 $code .= $this->generateMethod($method)."\n"; 59 } 60 $code .= "\n}"; 61 62 return sprintf("namespace %s {\n%s\n}", $namespace, $code); 63 } 64 65 private function generateMethod(Node\MethodNode $method) 66 { 67 $php = sprintf("%s %s function %s%s(%s)%s {\n", 68 $method->getVisibility(), 69 $method->isStatic() ? 'static' : '', 70 $method->returnsReference() ? '&':'', 71 $method->getName(), 72 implode(', ', $this->generateArguments($method->getArguments())), 73 $this->getReturnType($method) 74 ); 75 $php .= $method->getCode()."\n"; 76 77 return $php.'}'; 78 } 79 80 /** 81 * @return string 82 */ 83 private function getReturnType(Node\MethodNode $method) 84 { 85 if (version_compare(PHP_VERSION, '7.1', '>=')) { 86 if ($method->hasReturnType()) { 87 return $method->hasNullableReturnType() 88 ? sprintf(': ?%s', $method->getReturnType()) 89 : sprintf(': %s', $method->getReturnType()); 90 } 91 } 92 93 if (version_compare(PHP_VERSION, '7.0', '>=')) { 94 return $method->hasReturnType() && $method->getReturnType() !== 'void' 95 ? sprintf(': %s', $method->getReturnType()) 96 : ''; 97 } 98 99 return ''; 100 } 101 102 private function generateArguments(array $arguments) 103 { 104 $typeHintReference = $this->typeHintReference; 105 return array_map(function (Node\ArgumentNode $argument) use ($typeHintReference) { 106 $php = ''; 107 108 if (version_compare(PHP_VERSION, '7.1', '>=')) { 109 $php .= $argument->isNullable() ? '?' : ''; 110 } 111 112 if ($hint = $argument->getTypeHint()) { 113 $php .= $typeHintReference->isBuiltInParamTypeHint($hint) ? $hint : '\\'.$hint; 114 } 115 116 $php .= ' '.($argument->isPassedByReference() ? '&' : ''); 117 118 $php .= $argument->isVariadic() ? '...' : ''; 119 120 $php .= '$'.$argument->getName(); 121 122 if ($argument->isOptional() && !$argument->isVariadic()) { 123 $php .= ' = '.var_export($argument->getDefault(), true); 124 } 125 126 return $php; 127 }, $arguments); 128 } 129} 130