@param \Protobuf\Compiler\Entity             $entity
@param \google\protobuf\FieldDescriptorProto $field

@return string[]
347708
public function generateBody(Entity $entity, FieldDescriptorProto $field)
    {
        $name      = $field->getName();
        $tag       = $field->getNumber();
        $extEntity = $this->getEntity($field->getExtendee());
        $extendee  = $extEntity->getNamespacedName();

        $nameQuoted     = var_export($name, true);
        $extendeeQuoted = var_export($extendee, true);

        $sizeCallback  = $this->generateSizeCallback($entity, $field);
        $readCallback  = $this->generateReadCallback($entity, $field);
        $writeCallback = $this->generateWriteCallback($entity, $field);
        $callbacks     = array_merge($readCallback, [null], $writeCallback, [null], $sizeCallback);
        $arguments     = [
            $extendeeQuoted,
            $nameQuoted,
            $tag,
            '$readCallback',
            '$writeCallback',
            '$sizeCallback',
            '__METHOD__'
        ];

        $body[] = 'if (self::$' . $name . ' !== null) {';
        $body[] = '    return self::$' . $name . ';';
        $body[] = '}';
        $body[] = null;
        $body   = array_merge($body, $callbacks);
        $body[] = null;
        $body[] = 'return self::$' . $name . ' = new \Protobuf\Extension\ExtensionField(' . implode(', ', $arguments) . ');';

        return $body;
    }
24317
protected function getConditions(EntityInterface $entity)
    {
        $parts = explode('\\', $this->getEntityDescriptor()->className());
        $property = lcfirst(array_pop($parts));
        return [
            "rel.{$this->getForeignKey()} = :{$property}" => [
                ":{$property}" => $entity->getId()
            ]
        ];
    }
179895
private static function getEntityProValue($entity, string $proName)
    {
        $tmpNodes     = \explode('_', $proName);
        $tmpNodes     = \array_map(function ($word) {
            return \ucfirst($word);
        }, $tmpNodes);
        $proName      = \implode('', $tmpNodes);
        $getterMethod = 'get' . $proName;

        if (!\method_exists($entity, $getterMethod)) {
            throw new \InvalidArgumentException('Entity object property getter method does not exist, properName=' . $proName);
        }

        return $entity->$getterMethod();
    }
508161
protected function isProtobufExtension(FieldDescriptorProto $fieldDescriptor)
    {
        if ( ! $fieldDescriptor->hasExtendee()) {
            return false;
        }

        $extendee = trim($fieldDescriptor->getExtendee(), '.');
        $package  = 'google.protobuf';
        $length   = strlen($package);

        return (substr($extendee, 0, $length) === $package);
    }
1140216
protected function getExistingFields($entity)
    {
        /** @var FieldValueRepository $repo */
        $repo = $this->em->getRepository(Entity\FieldValue::class);

        return $repo->getExistingFields($entity->getId(), $entity->getContenttype(), $this->mapping['fieldname']);
    }
498162
protected function generateExtensionField(Entity $entity, FieldDescriptorProto $field)
    {
        $name     = $field->getName();
        $number   = $field->getNumber();
        $docBlock = $this->getDocBlockType($field);
        $type     = $this->getFieldTypeName($field);
        $label    = $this->getFieldLabelName($field);
        $property = PropertyGenerator::fromArray([
            'static'       => true,
            'name'         => $name,
            'visibility'   => PropertyGenerator::VISIBILITY_PROTECTED,
            'docblock'     => [
                'shortDescription' => "Extension field : $name $label $type = $number",
                'tags'             => [
                    [
                        'name'        => 'var',
                        'description' => '\Protobuf\Extension',
                    ]
                ]
            ]
        ]);

        $property->getDocblock()->setWordWrap(false);

        return $property;
    }
490079
protected function generateReadCallback(Entity $entity, FieldDescriptorProto $field)
    {
        $body  = [];
        $sttm  = $this->generateFieldReadStatement($entity, $field);
        $lines = $this->addIndentation($sttm, 1);

        $body[] = '$readCallback = function (\Protobuf\ReadContext $context, $wire) {';
        $body[] = '    $reader = $context->getReader();';
        $body[] = '    $length = $context->getLength();';
        $body[] = '    $stream = $context->getStream();';
        $body[] = null;
        $body   = array_merge($body, $lines);
        $body[] = '};';

        return $body;
    }
1153509
public function getRelativePath($entity, $field)
    {
        if (is_object($entity)) {
            $entity = get_class($entity);
        }
        $entity = strtolower(Str::classname($entity));

        return $entity . '/' . $field;
    }
900168
protected function getDoctype(FieldDescriptorProto $field)
    {
        $fieldType = $field->getType()->value();
        $phpType   = Field::getPhpType($fieldType);
        $refTypes  = [
            Type::TYPE_ENUM_VALUE    => true,
            Type::TYPE_MESSAGE_VALUE => true
        ];

        if (isset($refTypes[$fieldType])) {
            $typeName  = $field->getTypeName();
            $refEntity = $this->getEntity($typeName);

            return $refEntity->getNamespacedName();
        }

        return $phpType ?: 'mixed';
    }
869734
protected function generateFields(Entity $entity)
    {
        $fields     = [];
        $descriptor = $entity->getDescriptor();
        $extensions = $descriptor->getExtensionList() ?: [];

        foreach ($extensions as $field) {
            $fields[] = $this->generateExtensionField($entity, $field);
        }

        return $fields;
    }
960801
protected function guessSingularField($entity, $field = null)
    {
        return $field ?: Inflector::singularize(
            lcfirst(basename(str_replace('\\', '/', $entity)))
        );
    }
184787
protected function generateRegisterAllExtensionsMethod(Entity $entity)
    {
        $lines      = [];
        $fields     = [];
        $descriptor = $entity->getDescriptor();
        $extensions = $descriptor->getExtensionList() ?: [];
        $messages   = $descriptor->getMessageTypeList() ?: [];

        foreach ($messages as $message) {

            if ( ! $message->hasExtensionList()) {
                continue;
            }

            foreach ($message->getExtensionList() as $extension) {
                $fields[] = $extension;
            }
        }

        foreach ($fields as $field) {
            $type  = $field->getTypeName();
            $name  = $this->getCamelizedName($field);

            $ref     = $this->getEntity($type);
            $class   = $ref->getNamespacedName();
            $lines[] = '$registry->add(' . $class . '::' . $name. '());';
        }

        foreach ($extensions as $field) {
            $type  = $field->getTypeName();
            $name  = $this->getCamelizedName($field);

            $lines[] = '$registry->add(self::' . $name . '());';
        }

        $body       = implode(PHP_EOL, $lines);
        $method     = MethodGenerator::fromArray([
            'static'     => true,
            'body'       => $body,
            'name'       => 'registerAllExtensions',
            'parameters' => [
                [
                    'name' => 'registry',
                    'type' => '\Protobuf\Extension\ExtensionRegistry'
                ]
            ],
            'docblock'   => [
                'shortDescription' => "Register all extensions",
                'tags'             => [
                    [
                        'name'        => 'param',
                        'description' => '\Protobuf\Extension\ExtensionRegistry',
                    ]
                ]
            ]
        ]);

        return $method;
    }
942668
protected function generateAddMethod(Entity $entity, FieldDescriptorProto $field)
    {
        $fieldName  = $field->getName();
        $fieldType  = $field->getType();
        $collClass  = $this->getCollectionClassName($field);
        $methodName = 'add' . $this->getClassifiedName($field);
        $typeHint   = ($fieldType !== Type::TYPE_BYTES())
            ? $this->getDoctype($field)
            : null;

        $lines[] = 'if ($this->' . $fieldName . ' === null) {';
        $lines[] = '    $this->' . $fieldName . ' = new ' . $collClass . '();';
        $lines[] = '}';
        $lines[] = null;
        $lines[] = ($fieldType !== Type::TYPE_BYTES())
            ? '$this->' . $fieldName . '->add($value);'
            : '$this->' . $fieldName . '->add(\Protobuf\Stream::wrap($value));';

        return MethodGenerator::fromArray([
            'name'       => $methodName,
            'body'       => implode(PHP_EOL, $lines),
            'parameters' => [
                [
                    'name'   => 'value',
                    'type'   => $typeHint
                ]
            ],
            'docblock'   => [
                'shortDescription' => "Add a new element to '$fieldName'",
                'tags'             => [
                    [
                        'name'        => 'param',
                        'description' => $this->getDoctype($field) . ' $value'
                    ]
                ]
            ]
        ]);
    }
1292229
protected function _extractDirtyFields(EntityInterface $entity): array
    {
        $dirtyFields = [];
        $fields = array_keys($entity->toArray());
        $dirtyFields = $entity->extract($fields, true);
        unset($dirtyFields['modified']);

        return array_keys($dirtyFields);
    }
992083
protected function generateRequiredFieldException(Entity $entity, FieldDescriptorProto $field)
    {
        $name       = $field->getName();
        $label      = $field->getLabel();
        $tag        = $field->getNumber();
        $isRequired = $label === Label::LABEL_REQUIRED();

        if ( ! $isRequired) {
            return [];
        }

        $class   = $entity->getNamespacedName();
        $format  = 'Field "%s#%s" (tag %s) is required but has no value.';
        $message = var_export(sprintf($format, $class, $name, $tag), true);

        $body[] = 'if ($this->' . $name . ' === null) {';
        $body[] = '    throw new \UnexpectedValueException(' . $message . ');';
        $body[] = '}';
        $body[] = null;

        return $body;
    }
1280259
protected function getTypeHint(FieldDescriptorProto $field)
    {
        $type  = $this->getDoctype($field);
        $label = $field->getLabel();

        if ($label === Label::LABEL_REPEATED()) {
            return '\Protobuf\Collection';
        }

        return $type;
    }
284013
protected function generateSetterMethod(Entity $entity, FieldDescriptorProto $field)
    {
        $body       = [];
        $fieldName  = $field->getName();
        $fieldType  = $field->getType();
        $fieldLabel = $field->getLabel();
        $typeHint   = $this->getTypeHint($field);

        if ($fieldType === Type::TYPE_BYTES() && $fieldLabel !== Label::LABEL_REPEATED()) {
            $body[] = 'if ($value !== null && ! $value instanceof \Protobuf\Stream) {';
            $body[] = '    $value = \Protobuf\Stream::wrap($value);';
            $body[] = '}';
            $body[] = null;

            $typeHint = null;
        }

        $body[] = '$this->' . $fieldName . ' = $value;';

        $methodName = $this->getAccessorName('set', $field);
        $method     = MethodGenerator::fromArray([
            'name'       => $methodName,
            'body'       => implode(PHP_EOL, $body),
            'parameters' => [
                [
                    'name'   => 'value',
                    'type'   => $typeHint
                ]
            ],
            'docblock'   => [
                'shortDescription' => "Set '$fieldName' value",
                'tags'             => [
                    [
                        'name'        => 'param',
                        'description' => $this->getDocBlockType($field) . ' $value'
                    ]
                ]
            ]
        ]);

        $method->getDocblock()->setWordWrap(false);

        if ($fieldLabel !== Label::LABEL_REQUIRED()) {
            $method->getParameters()['value']->setDefaultValue(null);
        }

        return $method;
    }