private function generateMethods($methods)
{
$source = '';
foreach ($methods as $method) {
$name = $method->name();
$nameLower = strtolower($name);
$nameExported = var_export($name, true);
$methodReflector = $method->method();
switch ($nameLower) {
case '__construct':
case '__destruct':
case '__call':
case '__callstatic':
continue 2;
}
$signature = $this->signatureInspector->signature($methodReflector);
if ($method->isCustom()) {
$parameterName = null;
foreach ($signature as $parameterName => $parameter) {
break;
}
if ('phonySelf' === $parameterName) {
array_shift($signature);
}
}
$parameterCount = count($signature);
$variadicIndex = -1;
$variadicReference = '';
if (empty($signature)) {
$argumentPacking = '';
} else {
$argumentPacking = "\n";
$index = -1;
foreach ($signature as $parameter) {
if ($parameter[2]) {
--$parameterCount;
$variadicIndex = ++$index;
$variadicReference = $parameter[1];
} else {
$argumentPacking .= "\n if (\$argumentCount > " . ++$index . ") {\n \$arguments[] = " . $parameter[1] . '$a' . $index . ";\n }";
}
}
}
if ($this->isReturnTypeSupported && $methodReflector->hasReturnType()) {
$type = $methodReflector->getReturnType();
$isBuiltin = $type->isBuiltin();
if ($this->isHhvm) {
// @codeCoverageIgnoreStart
$typeString = $methodReflector->getReturnTypeText();
// TODO: review HHVM usage of ? prefix
if (0 === strpos($typeString, '?')) {
$typeString = '';
} else {
$genericPosition = strpos($typeString, '<');
if (false !== $genericPosition) {
$typeString = substr($typeString, 0, $genericPosition);
}
}
$isBuiltin = $isBuiltin && false === strpos($typeString, '\\');
// @codeCoverageIgnoreEnd
} else {
if ($type->allowsNull()) {
$typeString = '?' . $type;
// @codeCoverageIgnore
} else {
$typeString = (string) $type;
}
}
if ('self' === $typeString) {
$typeString = $methodReflector->getDeclaringClass()->getName();
}
if ($isBuiltin) {
$returnType = ' : ' . $typeString;
// TODO: remove once PHP 7.1 is used for coverage
// @codeCoverageIgnoreStart
} elseif ($this->isNullableTypeSupported && 0 === strpos($typeString, '?')) {
$returnType = ' : ?\\' . substr($typeString, 1);
// @codeCoverageIgnoreEnd
} else {
$returnType = ' : \\' . $typeString;
}
$isVoidReturn = $isBuiltin && 'void' === $typeString;
} else {
$returnType = '';
$isVoidReturn = false;
}
$isStatic = $method->isStatic() ? 'static ' : '';
if ($isStatic) {
$handle = 'self::$_staticHandle';
} else {
$handle = '$this->_handle';
}
$body = " \$argumentCount = \\func_num_args();\n" . ' $arguments = array();' . $argumentPacking . "\n\n for (\$i = " . $parameterCount . "; \$i < \$argumentCount; ++\$i) {\n";
if ($variadicIndex > -1) {
$body .= " \$arguments[] = {$variadicReference}\$a" . "{$variadicIndex}[\$i - {$variadicIndex}];\n";
} else {
$body .= " \$arguments[] = \\func_get_arg(\$i);\n";
}
$body .= " }\n\n if (!{$handle}) {\n";
// TODO: remove once PHP 7.1 is used for coverage
// @codeCoverageIgnoreStart
if ($isVoidReturn) {
$resultAssign = '';
} else {
// @codeCoverageIgnoreEnd
$resultAssign = '$result = ';
}
if ($isStatic) {
$body .= <<<EOD
{$resultAssign}\\call_user_func_array(
array(__CLASS__, 'parent::' . {$nameExported}),
\$arguments
);
EOD;
} else {
$body .= <<<EOD
{$resultAssign}\\call_user_func_array(
array(\$this, 'parent::' . {$nameExported}),
\$arguments
);
EOD;
}
// TODO: remove once PHP 7.1 is used for coverage
// @codeCoverageIgnoreStart
if ($isVoidReturn) {
$body .= "\n\n return;\n }\n\n" . " {$handle}->spy" . "(__FUNCTION__)->invokeWith(\n" . ' new \\Eloquent\\Phony\\Call\\Arguments' . "(\$arguments)\n );";
} else {
// @codeCoverageIgnoreEnd
$body .= "\n\n return \$result;\n }\n\n" . " \$result = {$handle}->spy" . "(__FUNCTION__)->invokeWith(\n" . ' new \\Eloquent\\Phony\\Call\\Arguments' . "(\$arguments)\n );\n\n" . ' return $result;';
}
$returnsReference = $methodReflector->returnsReference() ? '&' : '';
$source .= "\n " . $method->accessLevel() . ' ' . $isStatic . 'function ' . $returnsReference . $name;
if (empty($signature)) {
$source .= '()' . $returnType . "\n {\n";
} else {
$index = -1;
$isFirst = true;
foreach ($signature as $parameter) {
if ($isFirst) {
$isFirst = false;
$source .= "(\n ";
} else {
$source .= ",\n ";
}
$source .= $parameter[0] . $parameter[1] . $parameter[2] . '$a' . ++$index . $parameter[3];
}
$source .= "\n )" . $returnType . " {\n";
}
$source .= $body . "\n }\n";
}
return $source;
}