protected function generateSourceCode(ParsedType $type, SourceBuffer $buffer, \SplFileInfo $file) { $extends = array_map(function ($t) { return '\\' . ltrim($t, '\\'); }, $type->getExtends()); $extends = empty($extends) ? '' : implode(', ', $extends); $implements = ['\\' . MergedSourceInterface::class]; // Double inclusion safeguard: if ($type->isClass()) { $safe1 = sprintf(' if(!class_exists(%s, false)) { ', var_export($type->getName(), true)); $safe2 = ' } '; } elseif ($type->isInterface()) { $safe1 = sprintf(' if(!interface_exists(%s, false)) { ', var_export($type->getName(), true)); $safe2 = ' } '; } else { $safe1 = sprintf(' if(!trait_exists(%s, false)) { ', var_export($type->getName(), true)); $safe2 = ' } '; } $generator = new SourceGenerator($buffer); $generator->populateTypeMarker($type, ParsedType::MARKER_INTRODUCED_INTERFACES, implode(', ', $implements)); $generator->populateTypeMarker($type, ParsedType::MARKER_INTRODUCED_NAME, $type->getLocalName()); $generator->populateTypeMarker($type, ParsedType::MARKER_INTRODUCED_SUPERCLASS, $extends); $generator->populateTypeMarker($type, ParsedType::MARKER_INTRODUCED_PREPENDED_CODE, $safe1); $generator->populateTypeMarker($type, ParsedType::MARKER_INTRODUCED_EXTERNAL_CODE, $safe2); $code = trim($generator->generateCode($file->getPathname(), dirname($file->getPathname()))); $code = preg_replace("'^<\\?(?:php)?'i", '', $code) . "\n"; return $code; }
public function instrumentFile($sourceFile) { if ($this->manifest === NULL) { $this->manifest = $this->kernel->getManifest(); } $parsedFile = $this->manifest->getFile($sourceFile); $storage = $parsedFile->getStorage(); $generator = new SourceGenerator($parsedFile->getBuffer()); foreach ($storage->getTypes() as $parsedType) { $fields = $storage->getFields($parsedType); $methods = $storage->getMethods($parsedType); $typeInfo = new ParsedTypeInfo($parsedType, $fields, $methods); $instrumentation = new Instrumentation($this->manifest); $instrumentation->introduceInterface('KoolKode\\K2\\Instrument\\KoolTypeInterface'); if ($typeInfo->isClass()) { // Introduce instance type LSB marker: $instrumentation->introduceCode('protected static $K2TypeName = __CLASS__;'); // Introduce helper method trait: $instrumentation->introduceTrait('KoolKode\\K2\\Instrument\\KoolTypeTrait'); // Implement original source file lookup: $introduced = ' public static function K2GetSourceFile() { return '; $introduced .= var_export($sourceFile, true) . '; }'; $instrumentation->introduceCode($introduced); // Implement namespace context lookup: $introduced = ' public static function K2GetNamespaceContext() { '; $introduced .= 'static $ctx; if($ctx === NULL) { '; $introduced .= '$ctx = ' . $typeInfo->getNamespaceContext()->compile() . '; '; $introduced .= '} return $ctx; }'; $instrumentation->introduceCode($introduced); // Extended class introduction: $extends = $parsedType->getExtends(); if (!empty($extends)) { $generator->populateTypeMarker($parsedType, ParsedType::MARKER_INTRODUCED_SUPERCLASS, '\\' . array_shift($extends)); } $generator->populateTypeMarker($parsedType, ParsedType::MARKER_INTRODUCED_PREPENDED_CODE, ' if(!class_exists(' . var_export($parsedType->getName(), true) . ', false)) { '); } elseif ($typeInfo->isInterface()) { $generator->populateTypeMarker($parsedType, ParsedType::MARKER_INTRODUCED_EXTENDS, implode(', ', $parsedType->getExtends())); $generator->populateTypeMarker($parsedType, ParsedType::MARKER_INTRODUCED_PREPENDED_CODE, ' if(!interface_exists(' . var_export($parsedType->getName(), true) . ', false)) { '); } elseif ($typeInfo->isTrait()) { $generator->populateTypeMarker($parsedType, ParsedType::MARKER_INTRODUCED_PREPENDED_CODE, ' if(!trait_exists(' . var_export($parsedType->getName(), true) . ', false)) { '); } foreach ($this->instrumentors as $instrumentor) { $instrumentor->instrument($typeInfo, $instrumentation); } if ($instrumentation->hasInitializationCode()) { $this->buildConstructor($typeInfo, $instrumentation); } $traits = $instrumentation->getIntroducedTraits(); $traitCode = empty($traits) ? '' : 'use ' . implode(', ', $traits) . '; '; $generator->populateTypeMarker($parsedType, ParsedType::MARKER_INTRODUCED_NAME, $typeInfo->getLocalName()); $generator->populateTypeMarker($parsedType, ParsedType::MARKER_INTRODUCED_INTERFACES, implode(', ', $instrumentation->getIntroducedInterfaces())); $generator->populateTypeMarker($parsedType, ParsedType::MARKER_INTRODUCED_CODE, $traitCode . $instrumentation->getIntroducedCode()); $generator->populateTypeMarker($parsedType, ParsedType::MARKER_INTRODUCED_EXTERNAL_CODE, ' } ' . $instrumentation->getAppendedCode()); foreach ($typeInfo->getMethods() as $methodInfo) { $methodIns = $instrumentation->getMethodInstrumentation($methodInfo); if ($methodIns->count()) { $generator->populateMethodMarker($parsedType, $methodInfo->getParsedMethod(), ParsedMethod::MARKER_PREPENDED_CODE, $methodIns->getPrependedCode()); $generator->populateMethodMarker($parsedType, $methodInfo->getParsedMethod(), ParsedMethod::MARKER_APPENDED_CODE, $methodIns->getAppendedCode()); } } } return $generator->generateCode($sourceFile, dirname($sourceFile)); }