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)); }
public function parseFile($file, $key = NULL) { $mtime = filemtime($file); $hash = md5($file) . dechex(strlen($file)); $sql = 'SELECT * FROM "meta_file" WHERE "hash" = :hash AND "mtime" >= :mtime LIMIT 1'; $stmt = $this->pdo->prepare($sql); $stmt->bindValue('hash', $hash); $stmt->bindValue('mtime', $mtime); $stmt->execute(); $row = $stmt->fetch(\PDO::FETCH_ASSOC); if ($row !== false) { return new MetaFile($row['hash'], $row['location'], $row['mtime'], unserialize(gzdecode($row['storage'])), unserialize(gzdecode($row['buffer']))); } $storage = new ArraySourceStorage(); $parser = new SourceParser(); $buffer = $parser->parse(SourceStream::fromUrl($file), $storage); $this->insertFileStmt->execute(['hash' => $hash, 'location' => $file, 'mtime' => $mtime, 'key' => $key, 'storage' => gzencode(serialize($storage), 1), 'buffer' => gzencode(serialize($buffer), 1)]); foreach ($storage->getTypes() as $type) { $typeInfo = new ParsedTypeInfo($type, [], []); $this->insertTypeStmt->execute(['name' => $typeInfo->getName(), 'file' => $hash, 'mod' => MetaInfo::getTypeModifiers($typeInfo), 'mtime' => $mtime, 'data' => gzencode(serialize($type), 1)]); foreach ($type->getSupertypes() as $supertype) { $this->insertSupertypeStmt->execute(['super' => $supertype, 'sub' => $typeInfo->getName()]); } foreach ($typeInfo->getAnnotationCandidates() as $anno) { $this->insertTypeAnnotationStmt->execute(['type' => $typeInfo->getName(), 'anno' => $anno]); } foreach ($storage->getFields($type) as $field) { $fieldInfo = new ParsedFieldInfo($typeInfo, $field); $this->insertFieldStmt->execute(['type' => $type->getName(), 'field' => $fieldInfo->getName(), 'mod' => MetaInfo::getFieldModifiers($fieldInfo)]); foreach ($fieldInfo->getAnnotationCandidates() as $anno) { $this->insertFieldAnnotationStmt->execute(['type' => $typeInfo->getName(), 'field' => $field->getName(), 'anno' => $anno]); } } foreach ($storage->getMethods($type) as $method) { $methodInfo = new ParsedMethodInfo($typeInfo, $method); $this->insertMethodStmt->execute(['type' => $type->getName(), 'method' => $methodInfo->getName(), 'mod' => MetaInfo::getMethodModifiers($methodInfo)]); foreach ($methodInfo->getAnnotationCandidates() as $anno) { $this->insertMethodAnnotationStmt->execute(['type' => $type->getName(), 'method' => $methodInfo->getName(), 'anno' => $anno]); } } $comment = new DocComment($type->getComment()); foreach ($comment->getTagValues('merge') as $group) { $this->insertMergeGroupStmt->execute(['type' => $typeInfo->getName(), 'group' => trim($group)]); } $this->typeCache[strtolower($type->getName())] = new ParsedTypeInfo($type, $storage->getFields($type), $storage->getMethods($type)); } return new MetaFile($hash, $file, $mtime, $storage, $buffer); }