/**
  * @param \Protobuf\Stream $stream
  *
  * @return \Protobuf\Stream
  */
 public function compile(Stream $stream)
 {
     // Parse the request
     $request = CodeGeneratorRequest::fromStream($stream, $this->config);
     $response = new CodeGeneratorResponse();
     $context = $this->createContext($request);
     $entities = $context->getEntities();
     $options = $context->getOptions();
     $generator = new Generator($context);
     // whether or not it will renegerate classes with new extensions
     $regenerate = false;
     $hasExtension = $context->hasProtobufExtension();
     // Run each entity
     foreach ($entities as $key => $entity) {
         $generateImported = $options->getGenerateImported();
         $isFileToGenerate = $entity->isFileToGenerate();
         // Only compile those given to generate, not the imported ones
         if (!$generateImported && !$isFileToGenerate) {
             $this->logger->debug(sprintf('Skipping generation of imported class "%s"', $entity->getClass()));
             continue;
         }
         $this->logger->info(sprintf('Generating class "%s"', $entity->getClass()));
         $generator->visit($entity);
         $file = new File();
         $path = $entity->getPath();
         $content = $entity->getContent();
         $file->setName($path);
         $file->setContent($content);
         $response->addFile($file);
         if ($hasExtension && $this->loadEntityClass($entity)) {
             $regenerate = true;
         }
     }
     if ($regenerate) {
         $this->logger->info('Regenerating classes with new extensions');
         $stream->seek(0);
         // Renegerate classes with new extensions
         return $this->compile($stream);
     }
     $this->logger->info('Generation completed.');
     // Finally serialize the response object
     return $response->toStream($this->config);
 }
Example #2
0
 public function handle(CodeGeneratorRequest $request)
 {
     $files = array();
     foreach ((array) $request->getProtoFile() as $file) {
         /** @var FileDescriptorProto $file */
         $this->collectFile($file, array());
         $specClassName = "Skrz\\Meta\\MetaSpec";
         $customSpecClassName = false;
         foreach ($file->getSourceCodeInfo()->getLocation() as $location) {
             if ($location->getPath() === array(FileDescriptorProtoMeta::PACKAGE_PROTOBUF_FIELD) && $location->getLeadingComments() && preg_match("/@spec\\s+([a-zA-Z0-9_\\\\]+)/", $location->getLeadingComments(), $m)) {
                 $specClassName = $m[1];
                 $customSpecClassName = true;
             }
         }
         uksort($this->messages, function ($a, $b) {
             if (strlen($a) === strlen($b)) {
                 return strcmp($b, $a);
             }
             return strlen($b) - strlen($a);
         });
         $tmpFiles = array();
         foreach ($this->messages as $className => $message) {
             // compile message file
             $codeFile = new CodeGeneratorResponse\File();
             $codeFile->setName(str_replace("\\", "/", $className) . ".php");
             $result = $this->generateMessage($className, $message);
             $codeFile->setContent((string) $result->getFile());
             $files[$className] = $codeFile;
             // compile meta file
             if (!class_exists($className)) {
                 $tmpFiles[] = $tmpFile = tempnam(sys_get_temp_dir(), "protoc-gen-php");
                 file_put_contents($tmpFile, (string) $result->getFile());
                 require_once $tmpFile;
             }
             /** @var AbstractMetaSpec $spec */
             $spec = new $specClassName();
             if (!$customSpecClassName) {
                 $spec->match($className)->addModule(new ProtobufModule());
             }
             $metaResult = $spec->compile($type = Type::fromString($className));
             if ($metaResult !== null) {
                 $metaFile = new CodeGeneratorResponse\File();
                 $metaFile->setName(str_replace("\\", "/", $spec->createMetaClassName($type)) . ".php");
                 $metaFile->setContent((string) $metaResult->getFile());
                 $files[$metaResult->getClass()->getName()] = $metaFile;
             }
         }
         foreach ($tmpFiles as $tmpFile) {
             unlink($tmpFile);
         }
         foreach ($this->enums as $className => $enum) {
             $enumFile = new CodeGeneratorResponse\File();
             $enumFile->setName(str_replace("\\", "/", $className) . ".php");
             $result = $this->generateEnum($className, $enum);
             $enumFile->setContent((string) $result->getFile());
             $files[$className] = $enumFile;
         }
     }
     $response = new CodeGeneratorResponse();
     $response->setFile($files);
     return $response;
 }
 public function generate(proto\FileDescriptorProto $proto)
 {
     // Keep a reference to the current proto
     $this->proto = $proto;
     // Obtain the root namespace
     $ns = $proto->getPackage();
     // Reset the extensions dictionary
     $this->extensions = array();
     $result = array();
     // Generate Enums
     if (!empty($proto->enum_type)) {
         $result += $this->generateEnums($proto->enum_type, $ns);
     }
     // Generate Messages
     if (!empty($proto->message_type)) {
         $result += $this->generateMessages($proto->message_type, $ns);
     }
     // Collect extensions
     if (!empty($proto->extension_)) {
         foreach ($proto->extension_ as $field) {
             $this->extensions[$field->getExtendee()][] = $field;
         }
     }
     // Generate all extensions found in this proto file
     if (count($this->extensions)) {
         // In multifile mode we output all the extensions in a file named after
         // the proto file, since it's not trivial or even possible in all cases
         // to include the extensions with the extended message file.
         $fname = pathinfo($proto->name, PATHINFO_FILENAME) . '-extensions';
         $src = array();
         foreach ($this->extensions as $extendee => $fields) {
             $src[] = $this->template('extension', $fields, $extendee);
         }
         $result[$fname] = implode("\n", $src);
     }
     // Generate services
     if ($this->option('generic_services') && $proto->hasService()) {
         foreach ($proto->getServiceList() as $service) {
             $src = $this->template('service', $service, $ns);
             $result[$namespace . '.' . $service->getName()] = $src;
         }
     }
     $suffix = $this->option('suffix', '.php');
     $files = array();
     if ($this->option('multifile', false)) {
         foreach ($result as $ns => $content) {
             if (empty($content)) {
                 continue;
             }
             // Generate a filename from the mapped namespace
             $fname = str_replace($this->nsSep, DIRECTORY_SEPARATOR, $this->ns($ns));
             $fname .= $suffix;
             $file = new proto\compiler\CodeGeneratorResponse\File();
             $file->setName($fname);
             $src = $this->template('file', $content, $ns);
             $file->setContent($src);
             $files[] = $file;
         }
     } else {
         $fname = pathinfo($proto->name, PATHINFO_FILENAME) . $suffix;
         $file = new \google\protobuf\compiler\CodeGeneratorResponse\File();
         $file->setName($fname);
         $src = $this->template('file', implode("\n", $result), $ns);
         $file->setContent($src);
         $files[] = $file;
     }
     return $files;
 }